Как использовать функцию окна SQL для вычисления процента агрегата
мне нужно рассчитать проценты различных измерений в таблице. Я хотел бы упростить вещи, используя оконные функции для вычисления знаменателя, однако у меня проблема, потому что числитель также должен быть агрегатом.
в качестве простого примера возьмем следующую таблицу:
create temp table test (d1 text, d2 text, v numeric);
insert into test values ('a','x',5), ('a','y',5), ('a','y',10), ('b','x',20);
если я просто хочу рассчитать долю каждой отдельной строки из d1, то оконные функции работают нормально:
select d1, d2, v/sum(v) over (partition by d1)
from test;
"b";"x";1.00
"a";"x";0.25
"a";"y";0.25
"a";"y";0.50
однако, что мне нужно делать вычислить общую долю для суммы d2 из d1. Результат, который я ищу, таков:
"b";"x";1.00
"a";"x";0.25
"a";"y";0.75
поэтому я попробую это:
select d1, d2, sum(v)/sum(v) over (partition by d1)
from test
group by d1, d2;
однако, теперь я получаю сообщение об ошибке:
ERROR: column "test.v" must appear in the GROUP BY clause or be used in an aggregate function
Я предполагаю, что это потому, что он жалуется, что функция окна не учитывается в предложении grouping, однако функции windowing не могут быть помещены в предложение grouping в любом случае.
это использование Greenplum 4.1, который является вилкой Postgresql 8.4 и разделяет те же оконные функции. Обратите внимание, что Greenplum не может выполнять коррелированные подзапросы.
2 ответов
Я думаю, что вы на самом деле ищете это:
SELECT d1, d2, sum(v)/sum(sum(v)) OVER (PARTITION BY d1) AS share
FROM test
GROUP BY d1, d2;
производит требуемый результат.
применяются оконные функции после агрегатные функции. Внешнее sum()
на sum(sum(v))
является оконной функцией в этом примере и присоединяется к OVER ...
п., В то время как внутренний sum()
- это совокупность.
эффективно то же самое, что:
WITH x AS (
SELECT d1, d2, sum(v) AS sv
FROM test
GROUP BY d1, d2
)
SELECT d1, d2, sv/sum(sv) OVER (PARTITION BY d1) AS share
FROM x;
или (без CTE):
SELECT d1, d2, sv/sum(sv) OVER (PARTITION BY d1) AS share
FROM (
SELECT d1, d2, sum(v) AS sv
FROM test
GROUP BY d1, d2
) x;
или @Mu вариант.
в сторону: Greenplum представил коррелированные подзапросы с версией 4.2. см. в заметках о выпуске.
вам нужно сделать все это с помощью оконных функций? Похоже, вам просто нужно сгруппировать результат на d1
и d2
и затем суммировать суммы:
select d1, d2, sum(p)
from (
select d1, d2, v/sum(v) over (partition by d1) as p
from test
) as dt
group by d1, d2
это дает мне следующее:
d1 | d2 | sum
----+----+------------------------
a | x | 0.25000000000000000000
a | y | 0.75000000000000000000
b | x | 1.00000000000000000000