Estaba revisando un código antiguo escrito para PostgreSQL anterior a 8.4 , y vi algo realmente ingenioso. Recuerdo que una función personalizada hizo algo de esto en el día, pero olvidé cómo se array_agg()
veía previamente. Para su revisión, la agregación moderna se escribe así.
SELECT array_agg(x ORDER BY x DESC) FROM foobar;
Sin embargo, una vez, se escribió así,
SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);
Entonces, lo probé con algunos datos de prueba ...
CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
AS t(x);
Los resultados fueron sorprendentes. La forma #OldSchoolCool fue enormemente más rápida: una aceleración del 25%. Además, al simplificarlo sin ORDEN, se mostró la misma lentitud.
# 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)
Entonces, ¿qué está pasando aquí? ¿Por qué es array_agg , una función interna mucho más lenta que el vudú SQL del planificador?
Usando " PostgreSQL 9.5.5 en x86_64-pc-linux-gnu, compilado por gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005, 64 bits"
fuente
array_agg
debe realizar un seguimiento del orden de sus entradas donde elARRAY
constructor parece estar haciendo algo aproximadamente equivalente a aUNION
como una expresión interna. Si tuviera que aventurarme a adivinar,array_agg
probablemente requeriría más memoria. No pude probar exhaustivamente esto, pero en PostgreSQL 9.6 que se ejecuta en Ubuntu 16.04 laARRAY()
consultaORDER BY
utilizó una fusión externa y fue más lenta que laarray_agg
consulta. Como dijiste, antes de leer el código, tu respuesta es la mejor explicación que tenemos.array_agg()
es más rápido que el constructor de matrices? Por un caso simple? Muy improbable, pero si es así, probablemente porque Postgres basó su decisión para un plan de consulta en estadísticas inexactas de la configuración de costos. Nunca he vistoarray_agg()
superar a un constructor de matrices y lo he probado muchas veces.VACUUM ANALYZE
antes de ejecutar las consultas? Considere: dba.stackexchange.com/a/18694/3684Creo que la respuesta aceptada por Erwin podría agregarse con lo siguiente.
Por lo general, estamos trabajando con tablas regulares con índices, en lugar de tablas temporales (sin índices) como en la pregunta original. Es útil tener en cuenta que las agregaciones, como
ARRAY_AGG
, no pueden aprovechar los índices existentes cuando la clasificación se realiza durante la agregación .Por ejemplo, suponga la siguiente consulta:
Si tenemos un índice activado
t(id, ...)
, el índice podría usarse, a favor de una exploración secuencialt
seguida de una clasificación activadat.id
. Además, si la columna de salida que se envuelve en la matriz (aquíc
) es parte del índice (como un índice activadot(id, c)
o un índice de inclusión activadot(id) include(c)
), esto podría incluso ser una exploración de solo índice.Ahora, reescribamos esa consulta de la siguiente manera:
Ahora, la agregación no usará el índice y tiene que ordenar las filas en la memoria (o peor aún para conjuntos de datos grandes, en el disco). Esto siempre será una exploración secuencial
t
seguida de agregación + ordenación .Hasta donde yo sé, esto no está documentado en la documentación oficial, pero puede derivarse de la fuente. Este debería ser el caso para todas las versiones actuales, v11 incluido.
fuente
array_agg()
o similares funciones agregadas todavía puede índices de apalancamiento con una subconsulta como:SELECT ARRAY_AGG(c) FROM (SELECT c FROM t ORDER BY id) sub
. LaORDER BY
cláusula por agregado es lo que impide el uso del índice en su ejemplo. Un constructor de matriz es más rápido quearray_agg()
cuando cualquiera puede usar el mismo índice (o ninguno). Simplemente no es tan versátil. Ver: dba.stackexchange.com/a/213724/3684