sql >> Base de Datos >  >> RDS >> PostgreSQL

PostgreSQL:¿Cómo averiguar los números que faltan en una columna usando generate_series ()?

Datos de muestra dados:

create table results ( commandid integer primary key);
insert into results (commandid) select * from generate_series(1,1000);
delete from results where random() < 0.20;

Esto funciona:

SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE NOT EXISTS (SELECT 1 FROM results WHERE commandid = s.i);

al igual que esta formulación 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;

Ambos de los anteriores parecen dar como resultado planes de consulta idénticos en mis pruebas, pero debe comparar con sus datos en su base de datos usando EXPLAIN ANALYZE para ver cuál es mejor.

Explicación

Tenga en cuenta que en lugar de NOT IN He usado NOT EXISTS con una subconsulta en una formulación y un OUTER JOIN ordinario en el otro. Es mucho más fácil para el servidor de base de datos optimizarlos y evita los problemas confusos que pueden surgir con NULL s en NOT IN .

Inicialmente favorecí OUTER JOIN formulación, pero al menos en 9.1 con mis datos de prueba el NOT EXISTS el formulario se optimiza al mismo plan.

Ambos funcionarán mejor que el NOT IN siguiente formulación cuando la serie es grande, como en su caso. NOT IN solía requerir que Pg hiciera una búsqueda lineal de IN lista para cada tupla que se está probando, pero el examen del plan de consulta sugiere que Pg puede ser lo suficientemente inteligente como para codificarlo ahora. El NOT EXISTS (transformado en JOIN por el planificador de consultas) y JOIN trabajar mejor.

El NOT IN la formulación es confusa en presencia de NULL commandid s y puede ser ineficiente:

SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);

así que lo evitaría. Con 1 000 000 de filas, los otros dos se completaron en 1,2 segundos y el NOT IN la formulación se ejecutó vinculada a la CPU hasta que me aburrí y la cancelé.