PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

PostgreSQL:Como descobrir números ausentes em uma coluna usando generate_series()?


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

Isso 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);

assim como esta formulação 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 os itens acima parecem resultar em planos de consulta idênticos em meus testes, mas você deve comparar com seus dados em seu banco de dados usando EXPLAIN ANALYZE para ver qual é o melhor.

Explicação


Observe que em vez de NOT IN Eu usei NOT EXISTS com uma subconsulta em uma formulação e um OUTER JOIN comum no outro. É muito mais fácil para o servidor de banco de dados otimizá-los e evita os problemas confusos que podem surgir com NULL s em NOT IN .

Eu inicialmente favoreci o OUTER JOIN formulação, mas pelo menos em 9.1 com meus dados de teste o NOT EXISTS forma otimiza para o mesmo plano.

Ambos terão um desempenho melhor do que o NOT IN formulação abaixo quando a série é grande, como no seu caso. NOT IN costumava exigir que o Pg fizesse uma pesquisa linear do IN list para cada tupla sendo testada, mas o exame do plano de consulta sugere que Pg pode ser inteligente o suficiente para fazer o hash agora. O NOT EXISTS (transformado em um JOIN pelo planejador de consultas) e o JOIN trabalhe melhor.

O NOT IN formulação é confusa na presença de NULL commandid s e pode ser ineficiente:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);

então eu evitaria. Com 1.000.000 de linhas, as outras duas foram concluídas em 1,2 segundos e o NOT IN formulação funcionou vinculada à CPU até eu ficar entediado e cancelá-la.