Как использовать функцию окна 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