Eu estava revisando algum código antigo escrito para o PostgreSQL anterior à 8.4 e vi algo realmente bacana. Lembro-me de ter uma função personalizada para fazer isso antes, mas esqueci como array_agg()
era. Para revisão, a agregação moderna é escrita assim.
SELECT array_agg(x ORDER BY x DESC) FROM foobar;
No entanto, era uma vez, foi escrito assim,
SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);
Então, eu tentei com alguns dados de teste ..
CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
AS t(x);
Os resultados foram surpreendentes. A maneira #OldSchoolCool foi massivamente mais rápida: uma aceleração de 25%. Além disso, simplificá-lo sem a ORDEM, mostrou a mesma lentidão.
# EXPLAIN ANALYZE SELECT ARRAY(SELECT x FROM foobar);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Result (cost=104425.28..104425.29 rows=1 width=0) (actual time=1665.948..1665.949 rows=1 loops=1)
InitPlan 1 (returns $0)
-> Seq Scan on foobar (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.032..716.793 rows=10000000 loops=1)
Planning time: 0.068 ms
Execution time: 1671.482 ms
(5 rows)
test=# EXPLAIN ANALYZE SELECT array_agg(x) FROM foobar;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=119469.60..119469.61 rows=1 width=32) (actual time=2155.154..2155.154 rows=1 loops=1)
-> Seq Scan on foobar (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.031..717.831 rows=10000000 loops=1)
Planning time: 0.054 ms
Execution time: 2174.753 ms
(4 rows)
Então, o que está acontecendo aqui. Por que array_agg , uma função interna é muito mais lenta que o vodu SQL do planejador?
Usando o " PostgreSQL 9.5.5 no x86_64-pc-linux-gnu, compilado pelo gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005, 64 bits"
fonte
array_agg
deve acompanhar a ordem de suas entradas em que oARRAY
construtor parece estar fazendo algo aproximadamente equivalente a aUNION
como uma expressão internamente. Se eu tivesse que arriscar um palpite,array_agg
provavelmente exigiria mais memória. Não pude testar exaustivamente isso, mas no PostgreSQL 9.6 executando no Ubuntu 16.04, aARRAY()
consultaORDER BY
usou uma mesclagem externa e foi mais lenta que aarray_agg
consulta. Como você disse, antes de ler o código, sua resposta é a melhor explicação que temos.array_agg()
é mais rápido que o construtor de array? Para um caso simples? Muito improvável, mas, provavelmente, porque o Postgres baseou sua decisão para um plano de consulta em estatísticas imprecisas das configurações de custo. Eu nunca viarray_agg()
superar um construtor de matriz e testei várias vezes.VACUUM ANALYZE
isso antes de executar as consultas? Considere: dba.stackexchange.com/a/18694/3684Acredito que a resposta aceita por Erwin possa ser adicionada com o seguinte.
Normalmente, estamos trabalhando com tabelas regulares com índices, em vez de tabelas temporárias (sem índices), como na pergunta original. É útil observar que agregações, como
ARRAY_AGG
, não podem alavancar índices existentes quando a classificação é feita durante a agregação .Por exemplo, assuma a seguinte consulta:
Se tivermos um índice ativado
t(id, ...)
, o índice poderá ser usado, em favor de uma varredura sequencial ativadat
seguida por uma classificação ativadat.id
. Além disso, se a coluna de saída envolvida na matriz (aquic
) fizer parte do índice (como um índice ativadot(id, c)
ou um índice incluídot(id) include(c)
), isso pode até ser uma varredura apenas de índice.Agora, vamos reescrever essa consulta da seguinte maneira:
Agora, a agregação não usará o índice e precisará classificar as linhas na memória (ou pior ainda, para grandes conjuntos de dados, em disco). Essa sempre será uma varredura seqüencial
t
seguida de agregação + classificação .Até onde eu sei, isso não está documentado na documentação oficial, mas pode ser derivado da fonte. Esse deve ser o caso de todas as versões atuais, incluindo a v11.
fonte
array_agg()
ou funções agregadas semelhantes ainda pode índices de alavancagem com uma subconsulta como:SELECT ARRAY_AGG(c) FROM (SELECT c FROM t ORDER BY id) sub
. AORDER BY
cláusula por agregação é o que impede o uso do índice no seu exemplo. Um construtor de matriz é mais rápido do quearray_agg()
quando um deles pode usar o mesmo índice (ou nenhum). Não é tão versátil. Veja: dba.stackexchange.com/a/213724/3684