Daná ukázková data:
create table results ( commandid integer primary key);
insert into results (commandid) select * from generate_series(1,1000);
delete from results where random() < 0.20;
Toto funguje:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE NOT EXISTS (SELECT 1 FROM results WHERE commandid = s.i);
stejně jako tato alternativní formulace:
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;
Zdá se, že oba výše uvedené vedou v mých testech k identickým plánům dotazů, ale měli byste je porovnat s daty ve vaší databázi pomocí EXPLAIN ANALYZE
abyste viděli, která je nejlepší.
Vysvětlení
Všimněte si, že místo NOT IN
Použil jsem NOT EXISTS
s poddotazem v jedné formulaci a obyčejným OUTER JOIN
v tom druhém. Pro DB server je mnohem snazší je optimalizovat a vyhne se matoucím problémům, které mohou nastat s NULL
s v NOT IN
.
Původně jsem preferoval OUTER JOIN
formulace, ale alespoň v 9.1 s mými testovacími daty NOT EXISTS
formulář se optimalizuje podle stejného plánu.
Oba budou fungovat lépe než NOT IN
formulace níže, když je řada velká, jako ve vašem případě. NOT IN
používá se k požadavku, aby Pg provedl lineární vyhledávání IN
seznam pro každou testovanou n-tici, ale prozkoumání plánu dotazů naznačuje, že Pg může být dost chytrý na to, aby jej zahašoval hned. NOT EXISTS
(přeměněno na JOIN
plánovačem dotazů) a JOIN
pracovat lépe.
NOT IN
formulace je matoucí v přítomnosti NULL commandid
s a může být neefektivní:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);
tak bych se tomu vyhnul. S 1 000 000 řádky jsou další dva dokončeny za 1,2 sekundy a NOT IN
formulace běžela na CPU, dokud jsem se nezačal nudit a nezrušil jsem to.