И ещё раз о внешних ключах
Выяснилось, что мы можем повлиять на механику создания/валидации внешних ключей таким же образом, каким мы можем повлиять на план исполнения обычного SQL-запроса.
Мой тестовый пример (все имена реальных объектов заменены, планы и цифры реальные). Выполняю 3 варианта валидации FK c разными настройками сессии, план исполнения смотрю через расширение pg_query_state
.
Исполнение "по умолчанию" выбирается merge join.
ALTER TABLE child_t VALIDATE CONSTRAINT fk_child_parent;
select plan from pg_query_state(1483434);
plan
------------------------------------------------------------------------------------------------------------------------
Merge Anti Join (Current loop: actual rows=0, loop number=1) +
Merge Cond: (fk.entity_egrn_id = pk.id) +
-> Index Only Scan using child_t_idx1 on child_t fk (Current loop: actual rows=1, loop number=1) +
Index Cond: (entity_egrn_id IS NOT NULL) +
Heap Fetches: 1 +
-> Index Only Scan using parent_t_pkey on parent_t pk (Current loop: actual rows=113215, loop number=1)+
Heap Fetches: 113215
(1 row)
SET enable_mergejoin TO false;
ALTER TABLE child_t VALIDATE CONSTRAINT fk_child_parent;
select plan from pg_query_state(1483434);
plan
-----------------------------------------------------------------------------------------------
Hash Anti Join (Current loop: actual rows=0, loop number=1) +
Hash Cond: (fk.entity_egrn_id = pk.id) +
-> Seq Scan on child_t fk (Current loop: actual rows=1, loop number=1) +
Filter: (entity_id IS NOT NULL) +
-> Hash (Current loop: actual rows=0, loop number=1) +
Buckets: 67108864 Batches: 8 Memory Usage: 165802kB +
-> Seq Scan on parent_t pk (Current loop: actual rows=33979819, loop number=1)
SET enable_hashjoin TO false;
SET enable_mergejoin TO false;
ALTER TABLE child_t VALIDATE CONSTRAINT fk_child_parent;
select plan from pg_query_state(1483434);
plan
---------------------------------------------------------------------------------------------------
Nested Loop Anti Join (Current loop: actual rows=0, loop number=1) +
-> Seq Scan on child_t fk (Current loop: actual rows=329042, loop number=1) +
Filter: (entity_id IS NOT NULL) +
-> Index Only Scan using parent_t_pkey on parent_t pk (actual rows=1 loops=329041)+
Index Cond: (id = fk.entity_id) +
Heap Fetches: 329041
Почему всё это может быть важно:
1. Вы банально могли что-то делать в своей сессии и манипулировать её состоянием, например, "форсить" вычитку через Nested Loop, а затем запустить построение/валидацию FK на громадных таблицах.
2. По каким-то причинам PG выбрал неподходящий план, и вы знаете, что другой способ будет наверняка лучше :)