Дадени примерни данни:
create table results ( commandid integer primary key);
insert into results (commandid) select * from generate_series(1,1000);
delete from results where random() < 0.20;
Това работи:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE NOT EXISTS (SELECT 1 FROM results WHERE commandid = s.i);
както и тази алтернативна формулировка:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
LEFT OUTER JOIN results ON (results.commandid = s.i)
WHERE results.commandid IS NULL;
И двете от горните изглежда водят до идентични планове за заявки в моите тестове, но трябва да сравните с вашите данни във вашата база данни с помощта на EXPLAIN ANALYZE
за да видите кое е най-добро.
Обяснение
Имайте предвид, че вместо NOT IN
Използвал съм NOT EXISTS
с подзаявка в една формулировка и обикновен OUTER JOIN
в другата. За DB сървъра е много по-лесно да ги оптимизира и избягва объркващи проблеми, които могат да възникнат с NULL
s в NOT IN
.
Първоначално предпочитах OUTER JOIN
формулировка, но поне в 9.1 с моите тестови данни NOT EXISTS
формулярът се оптимизира към същия план.
И двете ще се представят по-добре от NOT IN
формулировка по-долу, когато серията е голяма, както е във вашия случай. NOT IN
използвани за изискване на Pg да извърши линейно търсене на IN
списък за всеки тестван кортеж, но изследването на плана на заявката предполага, че Pg може да е достатъчно умен, за да го хешира сега. NOT EXISTS
(трансформирано в JOIN
от инструмента за планиране на заявки) и JOIN
работят по-добре.
NOT IN
формулировката е объркваща и при наличието на NULL commandid
s и може да бъде неефективно:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);
така че бих го избегнал. С 1 000 000 реда другите два завършиха за 1,2 секунди и NOT IN
формулировката работеше в зависимост от процесора, докато не ми омръзна и я отмених.