私の(簡略化された)クエリ:
SELECT a.id FROM a LEFT JOIN b ON b.id = a.id WHERE b.id IS NULL ORDER BY id;
このようなクエリプランは機能します。
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Merge Anti Join (cost=0.57..3831.88 rows=128092 width=8)
Merge Cond: (a.id = b.id)
-> Index Only Scan using a_pkey on a (cost=0.42..3399.70 rows=130352 width=8)
-> Index Only Scan using b_pkey on b (cost=0.15..78.06 rows=2260 width=8)
(4 rows)
ただし、plannerがより良いと考えた場合、postgresql 9.5.9がシーケンシャルスキャンに切り替わることがあります(PostgreSQLがインデックス付き列でシーケンシャルスキャンを実行する理由を参照してください)。しかし、私の場合、それは事態を悪化させました。
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Merge Anti Join (cost=405448.22..39405858.08 rows=1365191502 width=8)
Merge Cond: (a.id = b.id)
-> Index Only Scan using a_pkey on a (cost=0.58..35528317.86 rows=1368180352 width=8)
-> Materialize (cost=405447.64..420391.89 rows=2988850 width=8)
-> Sort (cost=405447.64..412919.76 rows=2988850 width=8)
Sort Key: b.id
-> Seq Scan on b (cost=0.00..43113.50 rows=2988850 width=8)
(7 rows)
私の(ハック)解決策は、次の方法で順次スキャンを阻止することでした。
set enable_seqscan to off;
postgresqlのドキュメントによると、これを行う適切な方法は、ALTERTABLESPACEを使用してseq_page_costを実行することです。これは、インデックス付きの列でORDER BYを使用する場合に推奨される場合がありますが、よくわかりません。https://www.postgresql.org/docs/9.1/static/runtime-config-query.html