Dati di esempio dati:
create table results ( commandid integer primary key);
insert into results (commandid) select * from generate_series(1,1000);
delete from results where random() < 0.20;
Funziona:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE NOT EXISTS (SELECT 1 FROM results WHERE commandid = s.i);
così come questa formulazione alternativa:
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;
Entrambi i precedenti sembrano produrre piani di query identici nei miei test, ma dovresti confrontarli con i tuoi dati sul tuo database usando EXPLAIN ANALYZE
per vedere qual è il migliore.
Spiegazione
Nota che invece di NOT IN
Ho usato NOT EXISTS
con una sottoquery in una formulazione e un normale OUTER JOIN
nell'altro. È molto più facile per il server DB ottimizzarli ed evita i problemi di confusione che possono sorgere con NULL
s in NOT IN
.
Inizialmente ho preferito OUTER JOIN
formulazione, ma almeno in 9.1 con i miei dati di prova il NOT EXISTS
il modulo viene ottimizzato per lo stesso piano.
Entrambi avranno prestazioni migliori rispetto a NOT IN
formulazione di seguito quando la serie è grande, come nel tuo caso. NOT IN
utilizzato per richiedere a Pg di eseguire una ricerca lineare di IN
list per ogni tupla in fase di test, ma l'esame del piano di query suggerisce che Pg potrebbe essere abbastanza intelligente da eseguire l'hashing ora. Il NOT EXISTS
(trasformato in un JOIN
dal pianificatore di query) e il JOIN
funziona meglio.
Il NOT IN
la formulazione crea confusione in presenza di NULL commandid
se può essere inefficiente:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);
quindi lo eviterei Con 1.000.000 di righe le altre due completavano in 1,2 secondi e il NOT IN
la formulazione è stata eseguita con la CPU fino a quando non mi sono annoiato e l'ho annullato.