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.