テーブルt
には2つのインデックスがあります。
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)
;
any
演算子ではインデックスは使用されません:
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
ただし、そのうちの1つはin
演算子で使用されます。
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
レコードが正しいタイプにキャストされている場合は、レコードインデックスを使用します。
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
なぜプランナはany
演算子に非レコードインデックスを使用するので、in
演算子に非レコードインデックスを使用しないのですか?
内部的には、IN
の-2つの形式と、ANY
構成体があります。
setを取るそれぞれの1つは、他のものと同等であり、expr IN (<set>)
もexpr = ANY(<set>)
。詳細:
したがって、次の2つのクエリは同等であり、どちらもプレーンインデックスt_a_b_idx
を使用できます(クエリでインデックスを使用する場合はsolutionにすることもできます)。
EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));
または:
...
WHERE (a,b) IN (VALUES (1,1),(1,2));
どちらも同じ:
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
ただし、Postgresには「テーブル変数」がないため、これを関数に簡単に渡すことはできません。これはこのトピックを始めた問題につながります:
その問題にはさまざまな回避策があります。 1つは、私がそこに追加した代替回答です。他の何人か:
それぞれの2番目の形式は異なります。ANY
は実際のarrayを取りますが、IN
はコンマ区切りで受け取ります- 値のリスト。
これはtyping入力に対して異なる結果をもたらします。質問のEXPLAIN
出力でわかるように、次の形式です。
WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);
の省略形と見なされます:
ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])
そして、実際のROW値が比較されます。 Postgresは現在、複合型t_row_idx
のインデックスが適用可能であることを確認するほどスマートではありません。また、単純なインデックスt_a_b_idx
も同様に適用可能であることを認識していません。
明示的なキャストは、このスマートさの欠如を克服するのに役立ちます。
WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);
正しいオペランド(::int_pair[]
)のキャストはオプションです(ただし、パフォーマンスとあいまいさを避けるために望ましい)。左側のオペランドが既知のタイプになると、右側のオペランドは「匿名レコード」から一致するタイプに強制変換されます。そうして初めて、演算子が明確に定義されます。そして、Postgresはoperatorとleftオペランドに基づいて適切なインデックスを選択します。 COMMUTATOR
を定義する多くの演算子の場合、クエリプランナーはオペランドを反転して、インデックス付き式を左に移動できます。しかし、それはANY
構成要素では不可能です。
関連:
正規表現パターンを含むテキスト列に便利なインデックスを付ける方法はありますか?
WHERE (a,b) IN ((1,1),(1,2));
..値はelementsとして取得され、PostgresはEXPLAIN
出力でもう一度確認できるように、個々の整数値を比較できます。
Filter: ((b = 1) OR (b = 2))
したがって、Postgresは単純なインデックスt_a_b_idx
を使用できることがわかりました。
したがって、別の例の特定のケースの解決策があります。例のカスタム複合タイプint_pair
は、たまたまテーブルの行タイプt
自体、単純化できます。
CREATE INDEX t_row_idx2 ON t ((t));
次に、このクエリは明示的なキャストを行わずにインデックスを使用します。
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
ただし、通常の使用例では、暗黙的に存在するタイプのテーブル行を利用できません。