DB2 SQL – медиана с GROUP BY
-
12-09-2019 - |
Вопрос
Прежде всего, я использую DB2 для i5/OS V5R4.У меня есть ROW_NUMBER(), RANK() и общие табличные выражения.Я делаю нет иметь TOP n PERCENT или LIMIT OFFSET.
Фактический набор данных, с которым я работаю, трудно объяснить, поэтому давайте просто скажем, что у меня есть таблица истории погоды, в которой столбцы (city, temperature, timestamp)
.Я хочу сравнить медианы со средними значениями для каждой группы. (city)
.
Это был самый чистый способ получить медианное значение для всей совокупности таблиц.Я адаптировал его из IBM Redbook. здесь:
WITH base_t AS
( SELECT temp, row_number() over (order by temperature) AS rownum FROM t ),
count_t AS
( SELECT COUNT(temperature) + 1 AS base_count FROM base_t ),
median_t AS
( SELECT temperature FROM base_t, count_t
WHERE rownum in (FLOOR(base_count/2e0), CEILING(base_count/2e0)) )
SELECT DECIMAL(AVG(temperature),10,2) AS median FROM median_t
Это хорошо работает для возврата одной строки, но, похоже, не работает при группировке.Концептуально, это то, что я хочу:
SELECT city, AVG(temperature), MEDIAN(temperature) FROM ...
city | mean_temp | median_temp
===================================================
'Minneapolis' | 60 | 64
'Milwaukee' | 65 | 66
'Muskegon' | 70 | 61
Может быть ответ, который выставит меня глупым, но у меня ментальный блок, и это не моя задача №1, над которой я сейчас работаю.Кажется, что это возможно, но я не могу использовать что-то чрезвычайно сложное, поскольку это большая таблица, и мне нужна возможность настраивать, какие столбцы агрегируются.
Решение
В SQL Server агрегатные функции, такие как count(*), можно секционировать и вычислять без группировки.Я быстро просмотрел указанную красную книгу и обнаружил, что DB2 имеет ту же функцию.Но если нет, то это не сработает:
create table TemperatureHistory
(City varchar(20)
, Temperature decimal(5, 2)
, DateTaken datetime)
insert into TemperatureHistory values ('Minneapolis', 61, '20090101')
insert into TemperatureHistory values ('Minneapolis', 59, '20090102')
insert into TemperatureHistory values ('Milwaukee', 65, '20090101')
insert into TemperatureHistory values ('Milwaukee', 65, '20090102')
insert into TemperatureHistory values ('Milwaukee', 100, '20090103')
insert into TemperatureHistory values ('Muskegon', 80, '20090101')
insert into TemperatureHistory values ('Muskegon', 70, '20090102')
insert into TemperatureHistory values ('Muskegon', 70, '20090103')
insert into TemperatureHistory values ('Muskegon', 20, '20090104')
; with base_t as
(select city
, Temperature
, row_number() over (partition by city order by temperature) as RowNum
, (count(*) over (partition by city)) + 1 as CountPlusOne
from TemperatureHistory)
select City
, avg(Temperature) as MeanTemp
, avg(case
when RowNum in (FLOOR(CountPlusOne/2.0), CEILING(CountPlusOne/2.0))
then Temperature
else null end) as MedianTemp
from base_t
group by City