Tabel t
memiliki dua indeks:
create table t (a int, b int);
create type int_pair as (a int, b int);
create index t_row_idx on t (((a,b)::int_pair));
create index t_a_b_idx on t (a,b);
insert into t (a,b)
select i, i
from generate_series(1, 100000) g(i)
;
Tidak ada indeks yang digunakan dengan any
operator:
explain analyze
select *
from t
where (a,b) = any(array[(1,1),(1,2)])
;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Seq Scan on t (cost=0.00..1693.00 rows=1000 width=8) (actual time=0.042..126.789 rows=1 loops=1)
Filter: (ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
Rows Removed by Filter: 99999
Planning time: 0.122 ms
Execution time: 126.836 ms
Tetapi salah satunya digunakan dengan in
operator:
explain analyze
select *
from t
where (a,b) in ((1,1),(1,2))
;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Index Only Scan using t_a_b_idx on t (cost=0.29..8.32 rows=1 width=8) (actual time=0.028..0.029 rows=1 loops=1)
Index Cond: (a = 1)
Filter: ((b = 1) OR (b = 2))
Heap Fetches: 1
Planning time: 0.161 ms
Execution time: 0.066 ms
Ini menggunakan indeks catatan jika catatan dilemparkan ke jenis yang benar:
explain analyze
select *
from t
where (a,b)::int_pair = any(array[row(1,1),row(1,2)])
;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Index Scan using t_row_idx on t (cost=0.42..12.87 rows=2 width=8) (actual time=0.106..0.126 rows=1 loops=1)
Index Cond: (ROW(a, b)::int_pair = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
Planning time: 0.208 ms
Execution time: 0.203 ms
Mengapa perencana tidak menggunakan indeks non-rekaman untuk any
operator seperti yang digunakan untuk in
operator?
Jawaban:
Secara internal, ada dua bentuk terpisah dari
IN
, serta untukANY
membangun.Salah satu dari masing-masing, mengambil satu set , setara dengan yang lain dan
expr IN (<set>)
juga mengarah ke rencana kueri yang sama sepertiexpr = ANY(<set>)
yang dapat menggunakan indeks biasa. Detail:Akibatnya, dua kueri berikut ini sama dan keduanya dapat menggunakan indeks polos
t_a_b_idx
(yang juga bisa menjadi solusi jika Anda mencoba untuk mendapatkan kueri Anda untuk menggunakan indeks):Atau:
Identik untuk keduanya:
Namun , ini tidak dapat dengan mudah diteruskan ke suatu fungsi, karena tidak ada "variabel tabel" di Postgres. Yang mengarah ke masalah yang memulai topik ini:
Ada berbagai solusi untuk masalah itu. Salah satu jawaban alternatif yang saya tambahkan di sana. Beberapa lainnya:
Bentuk kedua dari masing-masing berbeda:
ANY
mengambil array aktual , sementaraIN
mengambil daftar nilai yang dipisahkan koma .Ini memiliki konsekuensi berbeda untuk mengetik input. Seperti yang dapat kita lihat di
EXPLAIN
output pertanyaan, formulir ini:dipandang sebagai singkatan untuk:
Dan nilai ROW aktual dibandingkan. Postgres saat ini tidak cukup pintar untuk melihat bahwa indeks pada tipe komposit
t_row_idx
berlaku. Juga tidak menyadari bahwa indeks sederhana jugat_a_b_idx
harus berlaku.Para pemeran yang eksplisit membantu mengatasi kekurangan kecerdasan ini:
Casting operan yang tepat (
::int_pair[]
) adalah opsional (meskipun lebih disukai untuk kinerja dan untuk menghindari ambiguitas). Setelah operan kiri memiliki jenis terkenal, operan kanan dipaksa dari "catatan anonim" ke jenis yang cocok. Hanya kemudian, operator didefinisikan dengan jelas. Dan Postgres mengambil indeks yang berlaku berdasarkan pada operator dan operan kiri . Untuk banyak operator yang mendefinisikanCOMMUTATOR
, perencana kueri dapat membalik operan untuk membawa ekspresi yang diindeks ke kiri. Tapi itu tidak mungkin denganANY
konstruknya.Terkait:
Apakah ada cara untuk mengindeks kolom teks yang berisi pola regex?
.. nilai diambil sebagai elemen dan Postgres dapat membandingkan nilai integer individual seperti yang dapat kita lihat dalam
EXPLAIN
output sekali lagi:Karenanya Postgres menemukan bahwa indeksnya sederhana
t_a_b_idx
dapat digunakan.Akibatnya, akan ada solusi lain untuk kasus tertentu dalam contoh : karena tipe komposit kustom
int_pair
dalam contoh tersebut setara dengan jenis baris tabelt
itu sendiri, kami dapat menyederhanakan:Maka kueri ini akan menggunakan indeks tanpa casting yang lebih eksplisit:
Tetapi kasus penggunaan umum tidak akan dapat menggunakan jenis baris tabel yang ada secara implisit.
sumber
IN(...)
daftar pendek dapat diterjemahkan (oleh perencana) ke dalam... OR ...
ekspresi dalam kasus di atas, biasanya hanya diterjemahkan ke dalamANY('{...}')
, yaitu menggunakan array. Jadi, dalam banyak kasus,IN
dengan daftar nilai danANY
dengan array adalah hal yang sama.IN(...)
tidak dapat diterjemahkan= ANY('{...}')
.