11

Ho bisogno di calcolare le percentuali di varie dimensioni in una tabella. Mi piacerebbe semplificare le cose usando le funzioni della finestra per calcolare il denominatore, tuttavia ho un problema perché il numeratore deve essere anche un aggregato.Come utilizzare una funzione della finestra SQL per calcolare una percentuale di un aggregato

Come semplice esempio, prendere la seguente tabella:

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); 

Se voglio solo per calcolare la quota di ogni singola riga di d1, funzioni poi windowing funzionano bene:

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 

Tuttavia, ciò che devo fare è calcolare la quota complessiva per la somma di d2 su d1. L'uscita sto cercando è questo:

"b";"x";1.00 
"a";"x";0.25 
"a";"y";0.75 

Così provo questo:

select d1, d2, sum(v)/sum(v) over (partition by d1) 
from test 
group by d1, d2; 

Tuttavia, ora ottengo un errore:

ERROR: column "test.v" must appear in the GROUP BY clause or be used in an aggregate function 

Sto assumendo questo è perché si lamenta che la funzione finestra non è considerata nella clausola di raggruppamento, tuttavia le funzioni di windowing non possono essere inserite nella clausola di raggruppamento.

Questo sta utilizzando Greenplum 4.1, che è un fork di Postgresql 8.4 e condivide le stesse funzioni di windowing. Si noti che Greenplum non può eseguire sottoquery correlate.

risposta

16

Penso che quello che in realtà si sta cercando è questo :

SELECT d1, d2, sum(v)/sum(sum(v)) OVER (PARTITION BY d1) AS share 
FROM test 
GROUP BY d1, d2; 

Produce il risultato richiesto.

Le funzioni di finestra vengono applicate dopo le funzioni di aggregazione. L'esterno sum() in sum(sum(v)) è una funzione finestra in questo esempio ed è collegato alla clausola OVER ... mentre il numero interno sum() è un aggregato.

effettivamente la stessa:

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; 

Or (senza 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; 

O @ variante di Mu.

A parte: Greenplum ha introdotto sottoquery correlate con la versione 4.2. See release notes.

+0

Ah fantastico! Questo è quello che stavo cercando. Ha senso. I documenti non sono così chiari su questa roba. – EvilPuppetMaster

+0

@erwinBrandsletter Salvato la mia vita! grazie – isJustMe

1

Hai bisogno di fare tutto con le funzioni della finestra? Suona come avete solo bisogno di gruppo il risultato si ha da d1 e d2 e poi sommare le somme:

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 

Questo mi dà questo:

d1 | d2 |   sum   
----+----+------------------------ 
a | x | 0.25000000000000000000 
a | y | 0.75000000000000000000 
b | x | 1.00000000000000000000 
+1

Ah, è vero, funziona davvero. La ragione per cui voglio farlo senza una sub-query è perché questo in realtà ha bisogno di entrare in uno strumento di BI (Tableau), e le subquery causano problemi. – EvilPuppetMaster

Problemi correlati