Dizin `= any ()` ile kullanılmaz, ancak 'in` ile kullanılır


15

Tabloda tiki dizin vardır:

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)
;

anyOperatör ile hiçbir dizin kullanılmaz :

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

Ancak bunlardan biri inoperatörle birlikte kullanılır :

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

Kayıt doğru tipte yayınlanmışsa kayıt dizinini kullanır:

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

Planlayıcı neden anyoperatör için kullandığı için kayıt dışı dizinini kullanmıyor in?


Bu ilginç soru SO ile ilgili bir tartışmadan kaynaklandı: stackoverflow.com/a/34601242/939860
Erwin Brandstetter

Yanıtlar:


13

Dahili olarak, orada iki ayrı formları arasında IN, yanı sıra ANYyapısı.

Her biri, bir küme alarak diğerine eşdeğerdir ve expr IN (<set>)aynı zamanda expr = ANY(<set>)düz bir dizin kullanabilenle aynı sorgu planına yol açar . Detaylar:

Sonuç olarak, aşağıdaki iki sorgu eşdeğerdir ve her ikisi de düz dizini kullanabilir t_a_b_idx(bu, sorguyu dizini kullanmak için almaya çalışıyorsanız da çözüm olabilir):

EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));

Veya:

...
WHERE (a,b) IN (VALUES (1,1),(1,2));

Her ikisi için de aynı:

                                                        QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.33..16.71 rows=1 width=8) (actual time=0.101..0.101 rows=0 loops=1)
   ->  Unique  (cost=0.04..0.05 rows=2 width=8) (actual time=0.068..0.070 rows=2 loops=1)
         ->  Sort  (cost=0.04..0.04 rows=2 width=8) (actual time=0.067..0.068 rows=2 loops=1)
               Sort Key: "*VALUES*".column1, "*VALUES*".column2
               Sort Method: quicksort  Memory: 25kB
               ->  Values Scan on "*VALUES*"  (cost=0.00..0.03 rows=2 width=8) (actual time=0.005..0.005 rows=2 loops=1)
   ->  Index Only Scan using t_plain_idx on t  (cost=0.29..8.32 rows=1 width=8) (actual time=0.009..0.009 rows=0 loops=2)
         Index Cond: ((a = "*VALUES*".column1) AND (b = "*VALUES*".column2))
         Heap Fetches: 0
 Planning time: 4.080 ms
 Execution time: 0.202 ms

Ancak , Postgres'de "tablo değişkenleri" olmadığından bu bir fonksiyona kolayca aktarılamaz. Bu da bu konuyu başlatan soruna yol açar:

Bu sorun için çeşitli geçici çözümler vardır. Biri oraya eklediğim alternatif cevap. Bazı diğerleri:


Her birinin ikinci biçimi farklıdır: ANYgerçek bir dizi alırken IN, virgülle ayrılmış değerler listesini alır .

Bunun girdiyi yazmak için farklı sonuçları vardır . EXPLAINSorunun çıktısında görebildiğimiz gibi , bu form:

WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);

için kısayol olarak görülüyor:

ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])

Ve gerçek ROW değerleri karşılaştırılır. Postgres şu anda bileşik türdeki dizinin t_row_idxuygulanabilir olduğunu görebilecek kadar akıllı değil . Ayrıca basit endeksin t_a_b_idxde geçerli olması gerektiğinin farkında değildir .

Açık bir kadro, bu zeki eksikliğinin üstesinden gelmeye yardımcı olur:

WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);

Doğru işleneni ( ::int_pair[]) kullanmak isteğe bağlıdır (performans ve belirsizlikleri önlemek için tercih edilir). Sol işlenen iyi bilinen bir türe sahip olduğunda, sağ işlenen "anonim kayıttan" eşleşen bir türe zorlanır. Ancak o zaman operatör açık bir şekilde tanımlanır. Postgres, operatöre ve soldaki işlenene göre geçerli dizinleri seçer . A tanımlayan birçok işleç için COMMUTATOR, sorgu planlayıcısı dizinlenmiş ifadeyi sola getirmek için işlenenleri çevirebilir. Ama bu ANYyapı ile mümkün değil .

İlişkili:

.. değerleri eleman olarak alınır ve Postgres EXPLAINçıktıda bir kez daha görebildiğimiz gibi tek tek tam sayı değerlerini karşılaştırabilir :

Filter: ((b = 1) OR (b = 2))

Bu nedenle Postgres basit dizinin t_a_b_idxkullanılabileceğini bulur .


Sonuç olarak, örnekte özel durum için başka bir çözüm olacaktır : örnekteki özel bileşik tür int_pair, tablonun tkendisinin satır türüne eşdeğer olduğundan, basitleştirebiliriz:

CREATE INDEX t_row_idx2 ON t ((t));

Sonra bu sorgu daha açık döküm olmadan dizini kullanır:

EXPLAIN ANALYZE
SELECT *
FROM   t
WHERE  t = ANY(ARRAY[(1,1),(1,2)]);
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=40.59..496.08 rows=1000 width=8) (actual time=0.19
1..0.191 rows=0 loops=1)
   Recheck Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
   ->  Bitmap Index Scan on t_row_idx2  (cost=0.00..40.34 rows=1000 width=0) (actual time=0.188..0.188 rows=0 loops=1)
         Index Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
 Planning time: 2.575 ms
 Execution time: 0.267 ms

Ancak tipik kullanım senaryoları, örtük olarak var olan tablo satırını kullanamaz.


1
Küçük bir ekleme: Kısa bir IN(...)liste (planlayıcı tarafından) ... OR ...yukarıdaki durumda bir ifadeye çevrilebilirken, genellikle sadece ANY('{...}')bir dizi kullanılarak çevrilir . Yani, çoğu durumda, INbir değer listesi ve ANYbir dizi ile aynı şeydir.
dezso

1
@dezso: Çoğu basit durumda, evet. Soru, tercüme IN(...) edilemeyen bir durumu göstermektedir = ANY('{...}').
Erwin Brandstetter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.