No PostgreSQL, geralmente há uma diferença bastante pequena em comprimentos de lista razoáveis, embora
IN é muito mais limpo conceitualmente. Muito longo AND ... <> ... listas e muito longas NOT IN listas ambos têm um desempenho terrível, com AND muito pior do que NOT IN . Em ambos os casos, se eles forem longos o suficiente para você fazer a pergunta, você deve fazer um teste de exclusão de antijunção ou subconsulta em uma lista de valores.
WITH excluded(item) AS (
VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT *
FROM thetable t
WHERE NOT EXISTS(SELECT 1 FROM excluded e WHERE t.item = e.item);
ou:
WITH excluded(item) AS (
VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT *
FROM thetable t
LEFT OUTER JOIN excluded e ON (t.item = e.item)
WHERE e.item IS NULL;
(Nas versões modernas do Pg, ambos produzirão o mesmo plano de consulta de qualquer maneira).
Se a lista de valores for longa o suficiente (muitas dezenas de milhares de itens), a análise de consulta pode começar a ter um custo significativo. Neste ponto, você deve considerar a criação de um
TEMPORARY tabela, COPY ndo os dados a serem excluídos, possivelmente criando um índice nele e, em seguida, usando uma das abordagens acima na tabela temporária em vez do CTE. Demonstração:
CREATE UNLOGGED TABLE exclude_test(id integer primary key);
INSERT INTO exclude_test(id) SELECT generate_series(1,50000);
CREATE TABLE exclude AS SELECT x AS item FROM generate_series(1,40000,4) x;
onde
exclude é a lista de valores a serem omitidos. Em seguida, comparo as seguintes abordagens nos mesmos dados com todos os resultados em milissegundos:
NOT INlista:3424.596AND ...lista:80173.823VALUESbaseado emJOINexclusão:20.727VALUESexclusão de subconsulta baseada:20.495-
JOINbaseado em tabela , nenhum índice na ex-lista:25.183 - Com base em tabela de subconsulta, sem índice na ex-lista:23.985
... tornando a abordagem baseada em CTE mais de três mil vezes mais rápida que o
AND list e 130 vezes mais rápido que o NOT IN Lista. Código aqui:https://gist.github.com/ringerc/5755247 (proteja seus olhos, você que segue este link).
Para este tamanho de conjunto de dados, adicionar um índice na lista de exclusão não fez diferença.
Notas:
INlista gerada comSELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;ANDlista gerada comSELECT string_agg(item::text, ' AND item <> ') from exclude;)- Exclusão de tabela baseada em subconsulta e junção foi praticamente a mesma em execuções repetidas.
- O exame do plano mostra que Pg traduz
NOT INpara<> ALL
Então... você pode ver que há um enorme intervalo entre ambos
IN e AND listas vs fazer uma junção adequada. O que me surpreendeu foi a rapidez com que fazer isso com um CTE usando um VALUES list estava... analisando os VALUES list não levou quase nenhum tempo, executando o mesmo ou ligeiramente mais rápido que a abordagem de tabela na maioria dos testes. Seria bom se o PostgreSQL pudesse reconhecer automaticamente um
IN absurdamente longo cláusula ou cadeia de AND semelhante condições e mude para uma abordagem mais inteligente, como fazer uma junção com hash ou transformá-la implicitamente em um nó CTE. No momento não sabe como fazer isso. Veja também:
- esta útil postagem de blog que Magnus Hagander escreveu sobre o assunto