Pergunta

Qual destas consultas é o mais rápido?

NÃO EXISTE:

SELECT ProductID, ProductName 
FROM Northwind..Products p
WHERE NOT EXISTS (
    SELECT 1 
    FROM Northwind..[Order Details] od 
    WHERE p.ProductId = od.ProductId)

ou não:

SELECT ProductID, ProductName 
FROM Northwind..Products p
WHERE p.ProductID NOT IN (
    SELECT ProductID 
    FROM Northwind..[Order Details])

O plano de execução de consulta diz que ambos fazem a mesma coisa. Se for esse o caso, que é a forma recomendada?

Este é baseado no banco de dados NorthWind.

[Edit]

Apenas encontrou este artigo útil: http://weblogs.sqlteam.com/mladenp/archive/2007 /05/18/60210.aspx

Eu acho que vou ficar com NÃO EXISTE.

Foi útil?

Solução

Eu sempre padrão para NOT EXISTS.

Os planos de execução pode ser o mesmo no momento, mas se qualquer coluna é alterada no futuro para permitir NULLs a versão NOT IN terá de fazer mais trabalho (mesmo que não NULLs estão realmente presentes nos dados) e a semântica de NOT IN se NULLs são presente não são susceptíveis de ser o que deseja de qualquer maneira.

Quando nem Products.ProductID ou [Order Details].ProductID permitir NULLs o NOT IN serão tratados de forma idêntica para a seguinte consulta.

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId) 

O plano exato pode variar, mas para os meus dados de exemplo eu recebo o seguinte.

Nem NULL

A razoavelmente equívoco comum parece ser que subconsultas correlacionadas são sempre "ruim" em comparação com junta. Eles certamente pode ser quando eles forçam um plano de loops aninhados (sub consulta fileira avaliadas por linha), mas este plano inclui um anti semi juntar operador lógico. semi Anti junta não se restringem a loops aninhados, mas pode usar hash ou merge (como neste exemplo) junta-se também.

/*Not valid syntax but better reflects the plan*/ 
SELECT p.ProductID,
       p.ProductName
FROM   Products p
       LEFT ANTI SEMI JOIN [Order Details] od
         ON p.ProductId = od.ProductId 

Se [Order Details].ProductID é NULL-able a consulta, em seguida, torna-se

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL) 

A razão para isto é que a semântica correta se [Order Details] contém quaisquer NULLs ProductId é retornar nenhum resultado. Veja os extras anti semi juntar e linha contagem carretel para verificar esta que é adicionado ao plano.

Um NULL

Se Products.ProductID também é alterado para se tornar NULL-able a consulta, em seguida, torna-se

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL)
       AND NOT EXISTS (SELECT *
                       FROM   (SELECT TOP 1 *
                               FROM   [Order Details]) S
                       WHERE  p.ProductID IS NULL) 

A razão para isso é porque uma NULL Products.ProductId não deve ser retornado nos resultados excepto se a consulta NOT IN sub foram para voltar há resultados em todos os (isto é, a tabela está vazia [Order Details]). Caso em que deveria. No plano para os meus dados de exemplo Isto é implementado pela adição de outros anti semi participar como abaixo.

Ambos NULL

O efeito disto é mostrado na o post já ligados por Buckley . No exemplo há o número de leituras lógicas aumento de cerca de 400 a 500.000.

Além disso, o fato de que uma única NULL pode reduzir a contagem de linhas de zero faz estimativa de cardinalidade muito difícil. Se SQL Server assume que isso vai acontecer, mas na verdade não havia filas NULL nos dados do resto do plano de execução pode ser catastroficamente pior, se isso é apenas parte de uma consulta mais ampla, com loops aninhados inadequadas que causam execução repetida de uma sub árvore caro por exemplo .

Este não é o plano de execução só é possível para um NOT IN sobre uma coluna NULL-capaz no entanto. Este artigo mostra uma outra para uma consulta no banco de dados AdventureWorks2008.

Para o NOT IN sobre uma coluna NOT NULL ou o NOT EXISTS contra qualquer uma coluna anulável anulável ou não dá o seguinte plano.

Não existe

Quando a coluna muda para NULL-able o plano NOT IN agora parece

Not In - Null

Ele adiciona um interior adicional operador de junção com o plano. Este aparelho é explicado aqui . É tudo o que há para converter o single anteriorÍndice correlacionada procurar em Sales.SalesOrderDetail.ProductID = <correlated_product_id> a duas procura cada linha externa. A única adicional é em WHERE Sales.SalesOrderDetail.ProductID IS NULL.

Como este está sob um anti semi juntar-se se aquele retorna todas as linhas da segunda procuram não ocorrerá. No entanto, se Sales.SalesOrderDetail não contém quaisquer NULLs ProductID que vai dobrar o número de buscar operações necessárias.

Outras dicas

Também estar ciente de que NOT IN não é equivalente a não existe quando se trata de null.

Este post explica isso muito bem

http://sqlinthewild.co.za/ index.php / 2010/02/18 / não-existe-vs-não-in /

Quando a subconsulta não retorna até mesmo um nulo, e não IN não corresponder a qualquer linhas.

A razão para isso pode ser encontrado por olhar para os detalhes do que o Não está em funcionamento realmente significa.

Vamos digamos, para fins de ilustração que existem 4 linhas na tabela chamada t, há uma coluna chamada ID com valores 1..4

WHERE SomeValue NOT IN (SELECT AVal FROM t)

é equivalente a

WHERE SomeValue != (SELECT AVal FROM t WHERE ID=1)
AND SomeValue != (SELECT AVal FROM t WHERE ID=2)
AND SomeValue != (SELECT AVal FROM t WHERE ID=3)
AND SomeValue != (SELECT AVal FROM t WHERE ID=4)

Vamos dizer também que Aval é NULL onde ID = 4. Assim que! = comparação retorna DESCONHECIDO. A tabela verdade lógica para os Estados e que desconhecido e verdadeiro é desconhecido, desconhecido e FALSE é FALSE. Há sim nenhum valor que pode ser AND'd com DESCONHECIDO para produzir o resultado VERDADEIRO

Assim, se qualquer linha de que subconsulta não retorna NULL, toda NOT IN operador irá avaliar a qualquer FALSE ou NULL e nenhum registro será retornou

Se o planejador de execução diz que eles são os mesmos, eles são o mesmo. Use qualquer um vai fazer a sua intenção mais óbvia -. Neste caso, o segundo

Na verdade, eu acredito que esta seria a mais rápida:

SELECT ProductID, ProductName 
    FROM Northwind..Products p  
          outer join Northwind..[Order Details] od on p.ProductId = od.ProductId)
WHERE od.ProductId is null

Eu tenho uma tabela que tem cerca de 120.000 registros e necessidade de selecionar apenas aqueles que não existe (combinado com uma coluna varchar) em quatro outras mesas com número de linhas aproximadamente 1500, 4000, 40000, 200. Todas as tabelas envolvidas tem índice exclusivo na coluna Varchar em causa.

NOT IN levou cerca de 10 minutos, NOT EXISTS levou 4 segundos.

Eu tenho uma consulta recursiva que poderia teve alguma seção desafinado que pode ter contribuído para os 10 minutos, mas a outra opção de tomar 4 segundos explica, pelo menos para mim que NOT EXISTS é muito melhor ou pelo menos que IN e EXISTS não são exatamente o mesmo e sempre vale a pena um cheque antes de ir adiante com o código.

No seu exemplo específico que eles são os mesmos, porque o otimizador descobriu o que você está tentando fazer é o mesmo em ambos os exemplos. Mas é possível que em exemplos não triviais o otimizador não pode fazer isso, e, nesse caso, há razões para preferir uma a outra na ocasião.

NOT IN deve ser preferido se você está testando várias linhas no seu select exterior. A subconsulta dentro da instrução NOT IN pode ser avaliada no início da execução, e a tabela temporária pode ser verificado em relação a cada valor no seleto exterior, ao invés de re-executar o subselect cada vez como seria necessário com a declaração NOT EXISTS.

Se a subconsulta deve ser correlacionados com a select exterior, então NOT EXISTS pode ser preferível, uma vez que o otimizador pode descobrir uma simplificação que impede a criação de quaisquer tabelas temporárias para executar a mesma função.

Eu estava usando

SELECT * from TABLE1 WHERE Col1 NOT IN (SELECT Col1 FROM TABLE2)

e descobriu que ele estava dando resultados errados (por errada eu quero dizer nenhum resultado). Como não havia um NULL em TABLE2.Col1.

Apesar de alterar a consulta para

SELECT * from TABLE1 T1 WHERE NOT EXISTS (SELECT Col1 FROM TABLE2 T2 WHERE T1.Col1 = T2.Col2)

me deu os resultados corretos.

Desde então eu comecei NÃO EXISTE usando todos os lugares.

Eles são muito semelhantes, mas não é realmente o mesmo.

Em termos de eficiência, eu encontrei o esquerda participar é nula declaração mais eficiente (quando uma abundância de linhas são para ser selecionado que é)

Se o otimizador diz que eles são o mesmo, em seguida, considerar o fator humano. Eu prefiro ver NÃO EXISTE:)

Depende ..

SELECT x.col
FROM big_table x
WHERE x.key IN( SELECT key FROM really_big_table );

não seria relativamente lento o não é muito para o tamanho limite do que a verificação de consulta para ver se eles chave é. EXISTE seria preferível neste caso.

Mas, dependendo otimizador das DBMS, este não poderia ser diferente.

Como um exemplo de quando EXISTE é melhor

SELECT x.col
FROM big_table x
WHERE EXISTS( SELECT key FROM really_big_table WHERE key = x.key);
  AND id = very_limiting_criteria
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top