Ich habe gerade einen alten Code überprüft, der für PostgreSQL vor 8.4 geschrieben wurde , und ich habe etwas wirklich Gutes gesehen. Ich erinnere mich, dass früher eine benutzerdefinierte Funktion etwas davon erledigte, aber ich vergaß, wie es vorher array_agg()
ausgesehen hatte. Zur Überprüfung wird die moderne Aggregation folgendermaßen geschrieben.
SELECT array_agg(x ORDER BY x DESC) FROM foobar;
Es war jedoch einmal so geschrieben:
SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);
Also habe ich es mit ein paar Testdaten versucht ..
CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
AS t(x);
Die Ergebnisse waren überraschend. Die #OldSchoolCool-Methode war enorm schneller: eine 25% ige Beschleunigung. Darüber hinaus zeigte die Vereinfachung ohne die BESTELLUNG die gleiche Langsamkeit.
# 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)
Also, was ist hier los? Warum ist array_agg , eine interne Funktion, so viel langsamer als der SQL-Voodoo des Planers?
Verwenden von " PostgreSQL 9.5.5 unter x86_64-pc-linux-gnu, kompiliert von gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005, 64-bit"
quelle
array_agg
muss die Reihenfolge seiner Eingaben verfolgen, in der derARRAY
KonstruktorUNION
intern etwas zu tun scheint, das ungefähr dem Ausdruck a entspricht. Wenn ich eine Vermutung wagenarray_agg
müsste , würde wahrscheinlich mehr Speicher benötigt. Ich konnte dies nicht ausführlich testen, aber auf PostgreSQL 9.6 unter Ubuntu 16.04 verwendete dieARRAY()
AbfrageORDER BY
eine externe Zusammenführung und war langsamer als diearray_agg
Abfrage. Wie Sie sagten, ist Ihre Antwort nach dem Lesen des Codes die beste Erklärung, die wir haben.array_agg()
es schneller geht als beim Array-Konstruktor? Für einen einfachen Fall? Sehr unwahrscheinlich, aber wenn ja, wahrscheinlich, weil Postgres seine Entscheidung für einen Abfrageplan auf einer ungenauen Statistik der Kosteneinstellungen beruhte. Ich habe noch nie gesehenarray_agg()
, einen Array-Konstruktor zu übertreffen, und ich habe viele Male getestet.VACUUM ANALYZE
es getan , bevor Sie die Abfragen ausgeführt haben? BeachtenIch glaube, die akzeptierte Antwort von Erwin könnte wie folgt ergänzt werden.
Normalerweise arbeiten wir mit regulären Tabellen mit Indizes anstelle von temporären Tabellen (ohne Indizes) wie in der ursprünglichen Frage. Beachten Sie, dass Aggregationen, wie z. B.
ARRAY_AGG
vorhandene Indizes, nicht nutzen können, wenn die Sortierung während der Aggregation durchgeführt wird .Nehmen Sie beispielsweise die folgende Abfrage an:
Wenn ein Index aktiviert ist
t(id, ...)
, kann der Index anstelle eines sequentiellen Scan-Vorgangst
und eines Sortiervorgangs verwendet werdent.id
. Wenn die Ausgabespalte, die in das Array (hierc
) eingeschlossen wird, Teil des Index ist (z. B. ein Index eint(id, c)
oder ein Einschlussindex eint(id) include(c)
), kann dies sogar ein Nur-Index-Scan sein.Nun schreiben wir diese Abfrage wie folgt um:
Jetzt verwendet die Aggregation den Index nicht mehr und muss die Zeilen im Speicher sortieren (oder noch schlimmer bei großen Datenmengen auf der Festplatte). Dies wird immer ein sequentieller Scan sein,
t
gefolgt von Aggregation + Sortierung .Soweit ich weiß, ist dies nicht in der offiziellen Dokumentation dokumentiert, sondern kann aus der Quelle abgeleitet werden. Dies sollte für alle aktuellen Versionen, einschließlich v11, der Fall sein.
quelle
array_agg()
oder ähnlichen Aggregatfunktionen können noch Leverage Indizes mit einer Unterabfrage wie:SELECT ARRAY_AGG(c) FROM (SELECT c FROM t ORDER BY id) sub
. Die Per-Aggregate-ORDER BY
Klausel schließt die Verwendung von Indizes in Ihrem Beispiel aus. Ein Array-Konstruktor ist schneller alsarray_agg()
wenn entweder derselbe Index verwendet werden kann (oder keiner). Es ist einfach nicht so vielseitig. Siehe: dba.stackexchange.com/a/213724/3684