Я должен выбрать строки в одной таблице (таблица_A довольно маленькая, ‹ 10 тыс. строк), которым нет соответствующих строк в другой таблице (таблица_B более 500 тыс. строк) на основе столбца_X (таблица_B имеет индекс btree для этого столбца). Если я использую следующий запрос:
select a.column1,
a.column2,
a.column3,
a.column_X,
b.column_X
from table_A a
left outer join table_B b on a.column_X = b.column_X
where a.column_X <> 0
and b.column_X is null
запрос (168 результирующих строк) выполняется примерно за 600 мс. Если, с другой стороны, я попробую другой запрос:
select column1,
column2,
column3,
column_X
from table_A
where column_X not in (
select column_X
from table_B
where column_X is not null
)
and column_X <> 0
для извлечения тех же 168 строк требуется около 8 минут. column_X имеет тип bigint, и приведение, похоже, не имеет значения (во втором запросе индекс никогда не используется). Есть идеи?
EXPLAIN ANALYZE
результатом — посетите веб-сайт explain.depesz.com, чтобы поделиться им. - person Pavel Stehule   schedule 14.02.2017where column_X is not null
в подзапросе не нужен. - person wildplasser   schedule 15.02.2017column_X
допускает значение NULL, то оно действительно необходимо. Без него, если столбец фактически содержит значениеNULL
, выражениеNOT IN
(и любое выражениеIN
) приведет кNULL
(это требуется стандартом ANSI SQL). И именно поэтому PostgreSQL не превратит этот запрос в антисоединение. - person pozs   schedule 15.02.2017column_X NOT IN (1, NULL)
эквивалентенcolumn_X <> 1 AND column_X <> NULL
, который эквивалентенcolumn_X <> 1 AND NULL
, который всегда будетNULL
. - person pozs   schedule 15.02.2017column_x NOT IN(1, NULL)
эквивалентенNOT ( column_x = 1 OR column_x = NULL)
, который стал быNOT (columns_x = 1 OR NULL)
ergo: даже если подзапрос возвращал нули, они никогда не могли сравниваться равными какому-либо значению (нулевому или ненулевому) - person wildplasser   schedule 15.02.2017