Таблица t
имеет два индекса:
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
Но один из них используется с 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
, а также дляANY
конструкции.Один из них, принимая набор , эквивалентен другому и
expr IN (<set>)
также приводит к тому же плану запросов, вexpr = ANY(<set>)
котором может использоваться простой индекс. Детали:Следовательно, следующие два запроса эквивалентны, и оба могут использовать простой индекс
t_a_b_idx
(который также может быть решением, если вы пытаетесь заставить свой запрос использовать индекс):Или:
Идентичны для обоих:
Однако это не может быть легко передано функции, так как в Postgres нет «табличных переменных». Что приводит к проблеме, с которой началась эта тема:
Существуют различные обходные пути для этой проблемы. Один из них - альтернативный ответ, который я добавил там. Некоторые другие:
Вторая форма каждого отличается:
ANY
принимает фактический массив , аIN
принимает список значений через запятую .Это имеет разные последствия для ввода ввода. Как мы видим в
EXPLAIN
выводе вопроса, эта форма:рассматривается как сокращение для:
И фактические значения ROW сравниваются. Postgres в настоящее время недостаточно умен, чтобы понять, что индекс для составного типа
t_row_idx
применим. Также он не понимает, что простой индекс такжеt_a_b_idx
должен быть применим.Явное приведение помогает преодолеть этот недостаток умов:
Приведение правильного операнда (
::int_pair[]
) является необязательным (хотя и предпочтительным для производительности и во избежание двусмысленности). Как только левый операнд имеет известный тип, правый операнд преобразуется из «анонимной записи» в соответствующий тип. Только тогда оператор определяется однозначно. И Postgres выбирает соответствующие индексы на основе оператора и левого операнда. Для многих операторов, которые определяют aCOMMUTATOR
, планировщик запросов может перевернуть операнды, чтобы перенести индексированное выражение влево. Но это невозможно сANY
конструкцией.Связанный:
Есть ли способ полезного индексирования текстового столбца, содержащего шаблоны регулярных выражений?
.. значения принимаются как элементы, и Postgres может сравнивать отдельные целочисленные значения, как мы видим в
EXPLAIN
выводе еще раз:Следовательно, Postgres считает, что
t_a_b_idx
можно использовать простой индекс .Следовательно, было бы другое решение для конкретного случая в примере : поскольку пользовательский составной тип
int_pair
в примере оказывается эквивалентным типу строки самой таблицыt
, мы могли бы упростить:Тогда этот запрос будет использовать индекс без более явного приведения:
Но типичные варианты использования не смогут использовать неявно существующий тип строки таблицы.
источник
IN(...)
список может быть преобразован (планировщиком) в... OR ...
выражение в вышеприведенном случае, он обычно просто переводитсяANY('{...}')
, то есть с использованием массива. Итак, в большинстве случаевIN
со списком значений иANY
с массивом это одно и то же.IN(...)
нельзя перевести на= ANY('{...}')
.