To support migration of existing queries, it would be nice not to have to rewrite EXISTS clauses as IN clauses. Here is one example of a query which optimizes poorly: DELETE FROM "CaseDispo" WHERE EXISTS ( SELECT * FROM "Consolidation" "C" WHERE "C"."caseNo" = '2006CM000123' AND "C"."xrefOrConsol" = 'C' AND "C"."countyNo" = 30 AND "CaseDispo"."caseNo" = "C"."crossRefCase" AND "CaseDispo"."countyNo" = "C"."countyNo" AND "CaseDispo"."dispoDate" = DATE '2005-10-31' ); Seq Scan on "CaseDispo" (cost=0.00..1227660.52 rows=176084 width=6) (actual time=501.557..501.557 rows=0 loops=1) Filter: (subplan) SubPlan -> Result (cost=0.00..3.46 rows=1 width=48) (actual time=0.000..0.000 rows=0 loops=352167) One-Time Filter: (($2)::date = '2005-10-31'::date) -> Index Scan using "Consolidation_pkey" on "Consolidation" "C" (cost=0.00..3.46 rows=1 width=48) (actual time=0.008..0.008 rows=0 loops=84) Index Cond: ((("caseNo")::bpchar = '2006CM000123'::bpchar) AND (($0)::bpchar = ("crossRefCase")::bpchar) AND (("countyNo")::smallint = 30) AND (($1)::smallint = ("countyNo")::smallint)) Filter: ("xrefOrConsol" = 'C'::bpchar) Total runtime: 501.631 ms (9 rows) To most programmers, it would be obvious that this is an exact logical equivalent to: DELETE FROM "CaseDispo" WHERE "countyNo" = 30 AND "dispoDate" = DATE '2005-10-31' AND "caseNo" IN ( SELECT "crossRefCase" FROM "Consolidation" "C" WHERE "C"."caseNo" = '2006CM000123' AND "C"."xrefOrConsol" = 'C' AND "C"."countyNo" = 30 ); Nested Loop (cost=7.02..10.50 rows=1 width=6) (actual time=0.036..0.036 rows=0 loops=1) -> HashAggregate (cost=7.02..7.03 rows=1 width=18) (actual time=0.034..0.034 rows=0 loops=1) -> Index Scan using "Consolidation_pkey" on "Consolidation" "C" (cost=0.00..7.02 rows=1 width=18) (actual time=0.032..0.032 rows=0 loops=1) Index Cond: ((("caseNo")::bpchar = '2006CM000123'::bpchar) AND (("countyNo")::smallint = 30)) Filter: ("xrefOrConsol" = 'C'::bpchar) -> Index Scan using "CaseDispo_pkey" on "CaseDispo" (cost=0.00..3.46 rows=1 width=24) (never executed) Index Cond: ((("CaseDispo"."caseNo")::bpchar = ("outer"."crossRefCase")::bpchar) AND (("CaseDispo"."dispoDate")::date = '2005-10-31'::date) AND (("CaseDispo"."countyNo")::smallint = 30)) Total runtime: 0.109 ms (8 rows) On this particular query, three orders of magnitude only gets you up to half a second, but the same thing happens on longer running queries. And even that half a second is significant when a user has to sit there and wait for the hourglass to clear on a regular basis. Clearly, the problem is not in the costing -- it recognizes the high cost of the EXISTS form. The problem is that it doesn't recognize that these are logically equivalent. Is there any work in progress to expand the set of plans examined for an EXISTS clause? If not, can we add such an enhancement to the TODO list? Do we need a good write-up on what optimizations are legal for EXISTS? -Kevin