Tengo una tabla que contiene dos columnas de permutaciones / combinaciones de matrices de enteros, y una tercera columna que contiene un valor, así:
CREATE TABLE foo
(
perm integer[] NOT NULL,
combo integer[] NOT NULL,
value numeric NOT NULL DEFAULT 0
);
INSERT INTO foo
VALUES
( '{3,1,2}', '{1,2,3}', '1.1400' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0.9280' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,2,1}', '{1,2,3}', '0' ),
( '{3,2,1}', '{1,2,3}', '0.8000' )
Quiero averiguar la desviación promedio y estándar para cada permutación, así como para cada combinación. Puedo hacer eso con esta consulta:
SELECT
f1.perm,
f2.combo,
f1.perm_average_value,
f2.combo_average_value,
f1.perm_stddev,
f2.combo_stddev,
f1.perm_count,
f2.combo_count
FROM
(
SELECT
perm,
combo,
avg( value ) AS perm_average_value,
stddev_pop( value ) AS perm_stddev,
count( * ) AS perm_count
FROM foo
GROUP BY perm, combo
) AS f1
JOIN
(
SELECT
combo,
avg( value ) AS combo_average_value,
stddev_pop( value ) AS combo_stddev,
count( * ) AS combo_count
FROM foo
GROUP BY combo
) AS f2 ON ( f1.combo = f2.combo );
Sin embargo, esa consulta puede ser bastante lenta cuando tengo muchos datos, porque la tabla "foo" (que en realidad consiste en 14 particiones cada una con aproximadamente 4 millones de filas) necesita ser escaneada dos veces.
Recientemente, aprendí que Postgres admite "Funciones de ventana", que es básicamente como un GROUP BY para una columna en particular. Modifiqué mi consulta para usarlos así:
SELECT
perm,
combo,
avg( value ) as perm_average_value,
avg( avg( value ) ) over w_combo AS combo_average_value,
stddev_pop( value ) as perm_stddev,
stddev_pop( avg( value ) ) over w_combo as combo_stddev,
count( * ) as perm_count,
sum( count( * ) ) over w_combo AS combo_count
FROM foo
GROUP BY perm, combo
WINDOW w_combo AS ( PARTITION BY combo );
Si bien esto funciona para la columna "combo_count", las columnas "combo_average_value" y "combo_stddev" ya no son precisas. Parece que se toma el promedio para cada permutación y luego se promedia una segunda vez para cada combinación, lo cual es incorrecto.
¿Cómo puedo arreglar esto? ¿Se pueden usar las funciones de ventana como una optimización aquí?
fuente
Respuestas:
Usted puede tener funciones de la ventana sobre el resultado de las funciones de agregado en un solo nivel de consulta.
Todo esto funcionaría bien después de algunas modificaciones, excepto que falla para la desviación estándar en el principio matemático . Los cálculos involucrados no son lineales, por lo que no puede simplemente combinar desviaciones estándar de subpoblaciones.
Porque
combo_average_value
necesitarías esta expresiónYa que necesita un promedio ponderado . (¡El promedio de un grupo con 10 miembros pesa más que el promedio de un grupo con solo 2 miembros!)
Esto funciona :
Estoy usando dos ventanas diferentes aquí, y reduzco las filas con las
DISTINCT
que se aplica incluso después de las funciones de la ventana.Pero dudo seriamente que sea más rápido que su consulta original. Estoy bastante seguro de que no lo es.
Mejor rendimiento con diseño de tabla alterado
Las matrices tienen una sobrecarga de 24 bytes (ligeras variaciones según el tipo). Además, parece tener bastantes elementos por matriz y muchas repeticiones. Para una mesa enorme como la suya, sería normalizar el esquema. Diseño de ejemplo:
Si no necesita integridad referencial, puede omitir las restricciones de clave externa.
La conexión
combo_id
también se podría colocar en la tablaperm
, pero en este escenario la almacenaría (ligeramente desnormalizada)value
para un mejor rendimiento.Esto daría como resultado un tamaño de fila de 32 bytes (encabezado de tupla + relleno: 24 bytes, 2 x int (8 bytes), sin relleno), más el tamaño desconocido de su
numeric
columna. (Si no necesita una precisión extrema, unadouble precision
o incluso unareal
columna también podrían necesitarlo ).Más sobre almacenamiento físico en esta respuesta relacionada en SO o aquí:
Configuración de PostgreSQL para rendimiento de lectura
De todos modos, eso es solo una fracción de lo que tiene ahora y haría que su consulta sea mucho más rápida solo por tamaño. Agrupar y ordenar enteros simples también es mucho más rápido.
Lo haría primero agregada en una subconsulta y luego unirse a
perm
ycombo
para un mejor rendimiento.fuente
foo
tabla que no eran relevantes. En realidad, hay varias columnas más que no se utilizan en esta consulta, por lo que no estoy convencido de que la normalización de las permutaciones y combinaciones proporcionaría un aumento de velocidad significativo, para este caso de uso en particular.