Saya baru saja meninjau beberapa kode lama yang ditulis untuk pra-8.4 PostgreSQL , dan saya melihat sesuatu yang sangat bagus. Saya ingat memiliki fungsi khusus melakukan beberapa hal ini pada hari itu, tapi saya lupaarray_agg()
. Untuk ulasan, agregasi modern ditulis seperti ini.
SELECT array_agg(x ORDER BY x DESC) FROM foobar;
Namun, pada suatu waktu, ditulis seperti ini,
SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);
Jadi, saya mencobanya dengan beberapa data uji ..
CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
AS t(x);
Hasilnya mengejutkan .. Cara #OldSchoolCool jauh lebih cepat: kecepatan 25%. Apalagi menyederhanakannya tanpa ORDER, menunjukkan kelambatan yang sama.
# 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)
Jadi, apa yang terjadi di sini. Mengapa array_agg , fungsi internal jauh lebih lambat dari voodoo SQL perencana?
Menggunakan " PostgreSQL 9.5.5 pada x86_64-pc-linux-gnu, dikompilasi oleh gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005, 64-bit"
sumber
array_agg
harus melacak urutan inputnya di manaARRAY
konstruktor tampaknya melakukan sesuatu yang kira-kira setara denganUNION
sebagai ekspresi internal. Jika saya harus berani menebak,array_agg
kemungkinan akan membutuhkan lebih banyak memori. Saya tidak bisa menguji ini secara mendalam tetapi pada PostgreSQL 9.6 berjalan pada Ubuntu 16.04ARRAY()
kueri denganORDER BY
menggunakan penggabungan eksternal dan lebih lambat dariarray_agg
kueri. Seperti yang Anda katakan, singkat membaca kode jawaban Anda adalah penjelasan terbaik yang kami miliki.array_agg()
yang lebih cepat daripada konstruktor array yang? Untuk kasus sederhana? Sangat tidak mungkin, tetapi jika demikian mungkin karena Postgres mendasarkan keputusannya untuk rencana permintaan pada statistik pengaturan biaya yang tidak akurat. Saya belum pernah melihatarray_agg()
mengungguli konstruktor array dan saya telah menguji berkali-kali.VACUUM ANALYZE
melakukannya sebelum menjalankan kueri? Pertimbangkan: dba.stackexchange.com/a/18694/3684Saya percaya jawaban yang diterima oleh Erwin dapat ditambahkan dengan yang berikut.
Biasanya, kami bekerja dengan tabel biasa dengan indeks, bukan tabel sementara (tanpa indeks) seperti pada pertanyaan awal. Penting untuk dicatat bahwa agregasi, seperti
ARRAY_AGG
, tidak dapat meningkatkan indeks yang ada saat penyortiran dilakukan selama agregasi .Misalnya, asumsikan kueri berikut:
Jika kita memiliki indeks aktif
t(id, ...)
, indeks dapat digunakan, mendukung pemindaian berurutant
diikuti oleh semacamt.id
. Selain itu, jika kolom output yang dibungkus dalam array (di sinic
) adalah bagian dari indeks (seperti indeks padat(id, c)
atau indeks termasuk padat(id) include(c)
), ini bahkan bisa menjadi hanya indeks-scan.Sekarang, mari kita tulis ulang kueri itu sebagai berikut:
Sekarang, agregasi tidak akan menggunakan indeks dan harus mengurutkan baris dalam memori (atau bahkan lebih buruk untuk set data besar, pada disk). Ini akan selalu menjadi pemindaian berurutan pada
t
diikuti oleh agregasi + semacam .Sejauh yang saya tahu, ini tidak didokumentasikan dalam dokumentasi resmi, tetapi dapat diturunkan dari sumbernya. Ini harus menjadi kasus untuk semua versi saat ini, termasuk v11.
sumber
array_agg()
atau fungsi agregat yang sama masih bisa memanfaatkan indeks dengan subquery seperti:SELECT ARRAY_AGG(c) FROM (SELECT c FROM t ORDER BY id) sub
.ORDER BY
Klausa per-agregat adalah apa yang menghalangi penggunaan indeks dalam contoh Anda. Konstruktor array lebih cepat daripadaarray_agg()
ketika bisa menggunakan indeks yang sama (atau tidak sama sekali). Hanya saja tidak serba guna. Lihat: dba.stackexchange.com/a/213724/3684