PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

PostgreSQL :Comment déterminer les nombres manquants dans une colonne à l'aide de generate_series() ?

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.