Optimizer: exists

現在の実装では、exists句のsubqueryはマージされないため、テーブルのJoin方法はNested loop joinになる。
Nested loop joinでは、Outer側(以下の例ではaccounts)から1行取り出すごとにsubqueryが実行されるため、Outer側のテーブルが大きい場合、検索が非常に遅くなる可能性がある。

select * from accounts a
where exists (select 'X' from history h where a.aid = h.aid)

Seq Scan on accounts a  (cost=0.00..227655.00 rows=50000 width=100)
  Filter: (subplan)
  SubPlan
    ->  Seq Scan on history h  (cost=0.00..2.25 rows=1 width=0)
          Filter: ($0 = aid)

手作業でexistsをinに書き換えると以下のようになる。

select * from accounts a
where a.aid in (select h.aid from history h)

Hash IN Join  (cost=2.25..3158.25 rows=100 width=100)
  Hash Cond: ("outer".aid = "inner".aid)
  ->  Seq Scan on accounts a  (cost=0.00..2655.00 rows=100000 width=100)
  ->  Hash  (cost=2.00..2.00 rows=100 width=4)
        ->  Seq Scan on history h  (cost=0.00..2.00 rows=100 width=4)

PostgreSQL 7.3まではinよりもexistsのほうが効率が良かったようであるが、7.4でinの処理が改善されため、7.4以降はinを使ったほうが効率がいいことが多いと思う。
Optimizerがexistsからinへの書き換えを行いinと同様の最適化がかかるようになればいいが、それまではexistsとinの両方を試して速い方を使うほうがいい。