Podane przykładowe dane:
create table results ( commandid integer primary key);
insert into results (commandid) select * from generate_series(1,1000);
delete from results where random() < 0.20;
To działa:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE NOT EXISTS (SELECT 1 FROM results WHERE commandid = s.i);
podobnie jak to alternatywne sformułowanie:
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;
Oba powyższe wydają się skutkować identycznymi planami zapytań w moich testach, ale powinieneś porównać je z danymi w Twojej bazie danych za pomocą EXPLAIN ANALYZE aby zobaczyć, co jest najlepsze.
Wyjaśnienie
Zauważ, że zamiast NOT IN Użyłem NOT EXISTS z podzapytanie w jednym sformułowaniu i zwykłym OUTER JOIN w innym. Serwer DB jest znacznie łatwiej zoptymalizować je i pozwala uniknąć mylących problemów, które mogą wystąpić przy NULL s w NOT IN .
Początkowo preferowałem OUTER JOIN formuła, ale przynajmniej w 9.1 z moimi danymi testowymi NOT EXISTS formularz optymalizuje się do tego samego planu.
Oba będą działać lepiej niż NOT IN sformułowanie poniżej, gdy seria jest duża, tak jak w twoim przypadku. NOT IN kiedyś wymagał od Pg wykonania liniowego przeszukiwania IN listę dla każdej testowanej krotki, ale analiza planu zapytań sugeruje, że Pg może być na tyle sprytny, aby go teraz zahaszować. NOT EXISTS (przekształcone w JOIN przez planer zapytań) i JOIN działa lepiej.
NOT IN sformułowanie jest mylące w obecności NULL commandid si może być nieefektywny:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);
więc unikałbym tego. Z 1 000 000 wierszy pozostałe dwa zostały ukończone w 1,2 sekundy, a NOT IN formuła działała na procesorze, dopóki się nie znudziłem i anulowałem.