Eu tenho uma tabela que contém duas colunas de permutações / combinações de matrizes inteiras e uma terceira coluna que contém um valor, assim:
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' )
Quero descobrir a média e o desvio padrão para cada permutação, bem como para cada combinação. Eu posso fazer isso com 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 );
No entanto, essa consulta pode ficar muito lenta quando tenho muitos dados, porque a tabela "foo" (que na realidade consiste em 14 partições cada uma com aproximadamente 4 milhões de linhas) precisa ser examinada duas vezes.
Recentemente, eu aprendi que o Postgres suporta "Funções de Janela", que é basicamente como um GROUP BY para uma coluna específica. Modifiquei minha consulta para usá-las da seguinte maneira:
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 );
Embora isso funcione para a coluna "combo_count", as colunas "combo_average_value" e "combo_stddev" não são mais precisas. Parece que a média está sendo calculada para cada permutação e, em seguida, calculada a média uma segunda vez para cada combinação, o que está incorreto.
Como posso consertar isso? As funções da janela podem ser usadas como uma otimização aqui?
fonte
Respostas:
Você pode ter funções de janela no resultado de funções agregadas em um único nível de consulta.
Tudo isso funcionaria bem depois de algumas modificações - exceto que falha no desvio padrão do princípio matemático . Os cálculos envolvidos não são lineares; portanto, você não pode simplesmente combinar desvios padrão de subpopulações.
Para
combo_average_value
você precisaria dessa expressãoComo você precisa de uma média ponderada . (A média de um grupo com 10 membros pesa mais que a média de um grupo com apenas 2 membros!)
Isso funciona :
Estou usando duas janelas diferentes aqui e reduzo as linhas com as
DISTINCT
quais é aplicada mesmo depois das funções da janela.Mas duvido seriamente que seja mais rápido que sua consulta original. Tenho certeza de que não é.
Melhor desempenho com layout de tabela alterado
As matrizes têm uma sobrecarga de 24 bytes (pequenas variações dependendo do tipo). Além disso, você parece ter alguns itens por matriz e muitas repetições. Para uma mesa enorme como a sua, pagaria para normalizar o esquema. Layout de exemplo:
Se você não precisar de integridade referencial, poderá omitir as restrições de chave estrangeira.
A conexão com
combo_id
também pode ser colocada na tabelaperm
, mas nesse cenário eu a armazenaria (ligeiramente desnormalizada)value
para obter um melhor desempenho.Isso resultaria em um tamanho de linha de 32 bytes (cabeçalho de tupla + preenchimento: 24 bytes, 2 x int (8 bytes), sem preenchimento), além do tamanho desconhecido da sua
numeric
coluna. (Se você não precisar de extrema precisão, umadouble precision
ou mesmo umareal
coluna também será necessária.)Mais sobre armazenamento físico nesta resposta relacionada ao SO ou aqui:
Configurando o PostgreSQL para desempenho de leitura
De qualquer forma, isso é apenas uma fração do que você tem agora e tornaria sua consulta muito mais rápida apenas por tamanho. Agrupar e classificar números inteiros simples também é muito mais rápido.
Você primeiro agregaria uma subconsulta e depois ingressaria no
perm
ecombo
para obter o melhor desempenho.fonte
foo
tabela que não eram relevantes. Na realidade, existem várias outras colunas que não são usadas por essa consulta, portanto, não estou convencido de que a normalização das permutações e combinações forneça um aumento de velocidade significativo para esse caso de uso específico.