Ya, operator tumpang tindih &&
bisa menggunakan indeks GIN pada array
. Sangat berguna untuk kueri yang satu ini untuk menemukan baris dengan orang tertentu (1
) di antara berbagai aktor:
SELECT * FROM eg_assoc WHERE actors && '{1}'::int[]
Namun , logika kueri Anda adalah sebaliknya, mencari semua orang yang terdaftar dalam array di eg_assoc
. Indeks GIN tidak membantu di sini. Kami hanya membutuhkan indeks btree dari PK person.id
.
Kueri yang tepat
Dasar-dasar:
Kueri berikut mempertahankan larik asli persis seperti yang diberikan , termasuk kemungkinan elemen duplikat dan urutan elemen asli. Berfungsi untuk array 1-dimensi . Dimensi tambahan dilipat menjadi satu dimensi. Lebih rumit untuk mempertahankan beberapa dimensi (tetapi sangat mungkin):
WITH ORDINALITY
di Postgres 9.4 atau lebih baru
SELECT aid, actors
, ARRAY(SELECT name
FROM unnest(e.actors) WITH ORDINALITY a(id, i)
JOIN eg_person p USING (id)
ORDER BY a.i) AS act_names
, benefactors
, ARRAY(SELECT name
FROM unnest(e.benefactors) WITH ORDINALITY b(id, i)
JOIN eg_person USING (id)
ORDER BY b.i) AS ben_names
FROM eg_assoc e;
LATERAL
kueri
Untuk PostgreSQL 9.3+ .
SELECT e.aid, e.actors, a.act_names, e.benefactors, b.ben_names
FROM eg_assoc e
, LATERAL (
SELECT ARRAY( SELECT name
FROM generate_subscripts(e.actors, 1) i
JOIN eg_person p ON p.id = e.actors[i]
ORDER BY i)
) a(act_names)
, LATERAL (
SELECT ARRAY( SELECT name
FROM generate_subscripts(e.benefactors, 1) i
JOIN eg_person p ON p.id = e.benefactors[i]
ORDER BY i)
) b(ben_names);
db<>fiddle di sini
dengan beberapa varian.
Lama sqlfiddle
Detail halus:Jika seseorang tidak ditemukan, itu hanya dijatuhkan. Kedua kueri ini menghasilkan array kosong ('{}'
) jika tidak ada orang yang ditemukan untuk seluruh array. Gaya kueri lainnya akan mengembalikan NULL
. Saya menambahkan varian ke biola.
Subkueri terkait
Untuk Postgres 8.4+ (di mana generate_subsrcipts()
diperkenalkan):
SELECT aid, actors
, ARRAY(SELECT name
FROM generate_subscripts(e.actors, 1) i
JOIN eg_person p ON p.id = e.actors[i]
ORDER BY i) AS act_names
, benefactors
, ARRAY(SELECT name
FROM generate_subscripts(e.benefactors, 1) i
JOIN eg_person p ON p.id = e.benefactors[i]
ORDER BY i) AS ben_names
FROM eg_assoc e;
Mungkin masih berkinerja terbaik, bahkan di Postgres 9.3.
The ARRAY
konstruktor
lebih cepat dari array_agg()
. Lihat:
Kueri Anda yang gagal
kueri yang disediakan oleh @a_horse sepertinya untuk melakukan pekerjaan itu, tetapi tidak dapat diandalkan, menyesatkan, berpotensi salah, dan tidak perlu mahal.
-
Gabung silang proxy karena dua gabungan yang tidak terkait. Sebuah anti-pola licik. Lihat:
Diperbaiki secara dangkal dengan
DISTINCT
diarray_agg()
untuk menghilangkan duplikat yang dihasilkan, tapi itu benar-benar menempatkan lipstik pada babi. Itu juga menghilangkan duplikat dalam aslinya karena saat ini tidak mungkin untuk membedakannya - yang berpotensi salah. -
Ekspresi
a_person.id = any(eg_assoc.actors)
berfungsi , tetapi menghilangkan duplikat dari hasil (terjadi dua kali dalam kueri ini), yang salah kecuali ditentukan. -
Urutan elemen larik asli tidak dipertahankan . Ini rumit secara umum. Tapi itu diperparah dalam kueri ini, karena aktor dan dermawan dikalikan dan dibuat berbeda lagi, yang menjamin perintah sewenang-wenang.
-
Tidak ada alias kolom di bagian luar
SELECT
menghasilkan nama kolom duplikat, yang membuat beberapa klien gagal (tidak bekerja di biola tanpa alias). -
min(actors)
danmin(benefactors)
tidak berguna. Biasanya orang hanya akan menambahkan kolom keGROUP BY
bukannya palsu-menggabungkan mereka. Tapieg_assoc.aid
adalah kolom PK (mencakup seluruh tabel dalamGROUP BY
), jadi itu bahkan tidak perlu. Hanyaactors, benefactors
.
Menggabungkan seluruh hasil membuang-buang waktu dan usaha untuk memulai. Gunakan kueri yang lebih cerdas yang tidak mengalikan baris dasar, lalu Anda tidak perlu menggabungkannya kembali.