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.