The following bug has been logged online: Bug reference: 4949 Logged by: Ole Tange Email address: postgresql....@tange.dk PostgreSQL version: 8.3.7 Operating system: Debian GNU/Linux Description: NOT IN is prohibitive slower than the rewrite for medium to large sets Details:
A NOT IN like this: SELECT foo FROM a WHERE key NOT IN (SELECT key FROM b); can always be rewritten like as: CREATE TEMPORARY TABLE c AS SELECT key FROM a; DELETE FROM c USING b WHERE c.key = b.key; SELECT foo FROM a,c WHERE a.key = c.key; (modulo NULLs which seem to always cause problems in NOT INs). Because it can be rewritten, NOT IN should never be much slower than the rewritten solution, as PostgreSQL should simply rewrite NOT IN to the above. However, the perl script below generates examples that clearly show this is not the case. These are my timings from a single system: $factor | Time for rewritten | Time for NOT IN 1000 | 00:00:00.03 | 00:00:00.01 3000 | 00:00:00.15 | 00:00:00.02 10000 | 00:00:00.51 | 00:10:26.20 30000 | 00:00:01.36 | 01:28:02.38 100000 | 00:00:06.27 | more than 8 hours So it seems NOT IN works fine for sets less than 10000. Where as NOT IN performs extremely poorly at sets > 10000. I suggest that PostgreSQL rewrites all NOT IN's when the set size reaches a certain limit (which on my system is around 10000). #!/usr/bin/perl one_run(1000); one_run(3000); one_run(10000); one_run(30000); one_run(100000); one_run(300000); sub one_run { my $factor = shift; print "select '======',$factor,'======';\n"; print "drop table a;\n"; print "begin;"; print "create table a (key text);\n"; for $x (0..6*$factor) { print "insert into a values ('foobarfoobar$x');\n" } print "commit;"; print "drop table b;\n"; print "begin;"; print "create table b (key text);\n"; for $x (4*$factor..7*$factor) { print "insert into b values ('foobarfoobar$x');\n" } print "commit;"; print " VACUUM FULL ANALYZE; SELECT 1,timeofday(); CREATE TEMPORARY TABLE c AS SELECT key FROM a; DELETE FROM c USING b WHERE c.key = b.key; SELECT COUNT(*) FROM a,c WHERE a.key = c.key; DROP TABLE c; SELECT 2,timeofday(); SELECT 3,timeofday(); SELECT COUNT(*) FROM a WHERE key NOT IN (SELECT key FROM b); SELECT 4,timeofday(); CREATE INDEX a_key ON a(key); SELECT 5,timeofday(); SELECT COUNT(*) FROM a WHERE key NOT IN (SELECT key FROM b); SELECT 6,timeofday(); CREATE INDEX b_key ON b(key); SELECT 7,timeofday(); SELECT COUNT(*) FROM a WHERE key NOT IN (SELECT key FROM b); SELECT 8,timeofday(); "; } -- Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-bugs