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.
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 NULL
s a versão NOT IN
terá de fazer mais trabalho (mesmo que não NULL
s estão realmente presentes nos dados) e a semântica de NOT IN
se NULL
s 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 NULL
s 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.
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 NULL
s ProductId
é retornar nenhum resultado. Veja os extras anti semi juntar e linha contagem carretel para verificar esta que é adicionado ao plano.
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 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.
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.
Quando a coluna muda para NULL
-able o plano NOT IN
agora parece
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 NULL
s 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