Exemple de données :
create table results ( commandid integer primary key);
insert into results (commandid) select * from generate_series(1,1000);
delete from results where random() < 0.20;
Cela fonctionne :
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE NOT EXISTS (SELECT 1 FROM results WHERE commandid = s.i);
tout comme cette formulation alternative :
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;
Les deux éléments ci-dessus semblent aboutir à des plans de requête identiques dans mes tests, mais vous devez comparer avec vos données sur votre base de données en utilisant EXPLAIN ANALYZE
pour voir lequel est le meilleur.
Explication
Notez qu'au lieu de NOT IN
J'ai utilisé NOT EXISTS
avec une sous-requête dans une formulation, et un OUTER JOIN
ordinaire dans l'autre. Il est beaucoup plus facile pour le serveur de base de données de les optimiser et cela évite les problèmes déroutants qui peuvent survenir avec NULL
s dans NOT IN
.
J'ai d'abord privilégié le OUTER JOIN
formulation, mais au moins en 9.1 avec mes données de test, le NOT EXISTS
le formulaire est optimisé selon le même plan.
Les deux fonctionneront mieux que le NOT IN
formulation ci-dessous lorsque la série est grande, comme dans votre cas. NOT IN
utilisé pour demander à Pg de faire une recherche linéaire du IN
list pour chaque tuple testé, mais l'examen du plan de requête suggère que Pg pourrait être assez intelligent pour le hacher maintenant. Le NOT EXISTS
(transformé en JOIN
par le planificateur de requêtes) et le JOIN
travailler mieux.
Le NOT IN
la formulation est à la fois confuse en présence de NULL commandid
s et peut être inefficace :
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);
donc je l'éviterais. Avec 1 000 000 lignes, les deux autres sont terminées en 1,2 seconde et le NOT IN
la formulation a été liée au processeur jusqu'à ce que je m'ennuie et que je l'annule.