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

SQL:Quando se trata de NOT IN e NOT EQUAL TO, qual é mais eficiente e por quê?


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 IN lista:3424.596
  • AND ... lista:80173.823
  • VALUES baseado em JOIN exclusão:20.727
  • VALUES exclusão de subconsulta baseada:20.495
  • JOIN baseado 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:
  • IN lista gerada com SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
  • AND lista gerada com SELECT 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 IN para <> 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