11

Necesito calcular porcentajes de varias dimensiones en una tabla. Me gustaría simplificar las cosas mediante el uso de funciones de ventana para calcular el denominador, sin embargo, estoy teniendo un problema porque el numerador tiene que ser un agregado también.Cómo usar una función de ventana SQL para calcular un porcentaje de un agregado

Como un simple ejemplo, adoptar la siguiente tabla:

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

Si sólo quiero calcular la cuota de cada fila individual de d1, funciones luego de ventanas funcionan bien:

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 

Sin embargo, lo que tengo que hacer es calcular la cuota total para la suma de d2 de d1. La salida Busco es la siguiente:

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

así que intento esto:

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

Sin embargo, ahora me sale un error:

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

Estoy suponiendo que esto se debe a se queja de que la función de ventana no se tiene en cuenta en la cláusula de agrupación, sin embargo, las funciones de ventana no se pueden poner en la cláusula de agrupación de todos modos.

Esto está utilizando Greenplum 4.1, que es una bifurcación de Postgresql 8.4 y comparte las mismas funciones de ventana. Tenga en cuenta que Greenplum no puede hacer subconsultas correlacionadas.

Respuesta

16

Creo que lo que en realidad se está buscando es este :

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

Produce el resultado solicitado.

Las funciones de ventana se aplican después de funciones de agregado. El sum() externo en sum(sum(v)) es una función de ventana en este ejemplo y se adjunta a la cláusula OVER ..., mientras que el sum() interno es un agregado.

efectivamente el mismo que:

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; 

O (sin 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 de Mu.

Aparte: Greenplum introdujo subconsultas correlacionadas con la versión 4.2. See release notes.

+0

¡Ah genial! Eso es lo que buscaba. Tiene sentido. Los documentos no son tan claros en esto. – EvilPuppetMaster

+0

@erwinBrandsletter ¡Salvó mi vida! Gracias – isJustMe

1

¿Necesita hacer todo esto con las funciones de la ventana? Parece que usted sólo tiene que agrupar el resultado que tiene por d1 y d2 y luego sumar las sumas:

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 

Eso me da esto:

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

Ah cierto, eso sí funciona. Sin embargo, la razón por la que quiero hacerlo sin una sub consulta es porque esto realmente necesita ir a una herramienta de BI (Tableau) y las subconsultas causan problemas. – EvilPuppetMaster

Cuestiones relacionadas