NO EN vs NO EXISTE
¿Cuál de estas consultas es más rápida?
NO EXISTE:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE NOT EXISTS (
SELECT 1
FROM Northwind..[Order Details] od
WHERE p.ProductId = od.ProductId)
O NO EN:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE p.ProductID NOT IN (
SELECT ProductID
FROM Northwind..[Order Details])
El plan de ejecución de consultas dice que ambos hacen lo mismo. Si ese es el caso, ¿cuál es la forma recomendada?
Esto se basa en la base de datos NorthWind.
[Editar]
Acabo de encontrar este artículo útil: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx
Creo que me quedaré con NO EXISTE.
Siempre uso por defecto NOT EXISTS
.
Los planes de ejecución pueden ser los mismos en este momento, pero si alguna de las columnas se modifica en el futuro para permitir NULL
s, la NOT IN
versión necesitará hacer más trabajo (incluso si en NULL
realidad no hay s presentes en los datos) y la semántica de NOT IN
si NULL
los s están presentes Es poco probable que sean los que desea de todos modos.
Cuando ninguno de los dos Products.ProductID
o lo [Order Details].ProductID
permite , se tratará de manera idéntica a la siguiente consulta.NULL
NOT IN
SELECT ProductID,
ProductName
FROM Products p
WHERE NOT EXISTS (SELECT *
FROM [Order Details] od
WHERE p.ProductId = od.ProductId)
El plan exacto puede variar, pero para los datos de mi ejemplo obtengo lo siguiente.
Un error razonablemente común parece ser que las subconsultas correlacionadas siempre son "malas" en comparación con las uniones. Ciertamente pueden serlo cuando fuerzan un plan de bucles anidados (subconsulta evaluada fila por fila), pero este plan incluye un operador lógico anti-semiunión. Las antisemiuniones no se limitan a bucles anidados, sino que también pueden utilizar uniones hash o fusionadas (como en este ejemplo).
/*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
Si [Order Details].ProductID
es NULL
-able la consulta se convierte en
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)
La razón de esto es que la semántica correcta, si [Order Details]
contiene algún NULL
ProductId
s, es no devolver resultados. Consulte el carrete adicional anti semi unión y recuento de filas para verificar que se agregue al plan.
Si Products.ProductID
también se cambia para volverse NULL
-able, la consulta se convierte en
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)
La razón de esto es que a NULL
Products.ProductId
no debe devolverse en los resultados, excepto si la NOT IN
subconsulta no arroja ningún resultado (es decir, la [Order Details]
tabla está vacía). En cuyo caso debería hacerlo. En el plan de mis datos de muestra, esto se implementa agregando otra anti semi unión como se muestra a continuación.
El efecto de esto se muestra en la publicación del blog ya vinculada por Buckley . En el ejemplo, el número de lecturas lógicas aumenta de aproximadamente 400 a 500.000.
Además, el hecho de que una sola NULL
pueda reducir el recuento de filas a cero hace que la estimación de la cardinalidad sea muy difícil. Si SQL Server supone que esto sucederá pero en realidad no había NULL
filas en los datos, el resto del plan de ejecución puede ser catastróficamente peor, si esto es solo parte de una consulta más grande, con bucles anidados inapropiados que causan la ejecución repetida de un subproceso costoso. árbol por ejemplo .
Sin embargo , este no es el único plan de ejecución posible para una NOT IN
columna NULL
capaz. Este artículo muestra otro para una consulta a la AdventureWorks2008
base de datos.
Para NOT IN
una NOT NULL
columna o NOT EXISTS
contra una columna que acepta valores NULL o no, proporciona el siguiente plan.
Cuando la columna cambia a NULL
-able, el NOT IN
plan ahora se ve así
Agrega un operador de unión interna adicional al plan. Este aparato se explica aquí . Todo está ahí para convertir la búsqueda de índice correlacionado único anterior Sales.SalesOrderDetail.ProductID = <correlated_product_id>
en dos búsquedas por fila exterior. El adicional está encendido WHERE Sales.SalesOrderDetail.ProductID IS NULL
.
Como esto se encuentra bajo una anti semi unión, si esa devuelve alguna fila, la segunda búsqueda no se producirá. Sin embargo, si Sales.SalesOrderDetail
no contiene ningún NULL
ProductID
correo electrónico, duplicará el número de operaciones de búsqueda requeridas.
También tenga en cuenta que NOT IN no es equivalente a NOT EXISTS cuando se trata de nulo.
Este post lo explica muy bien.
http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/
Cuando la subconsulta devuelve incluso un valor nulo, NOT IN no coincidirá con ninguna fila.
La razón de esto se puede encontrar observando los detalles de lo que realmente significa la operación NOT IN.
Digamos, a modo ilustrativo, que hay 4 filas en la tabla llamadas t, hay una columna llamada ID con valores 1..4
WHERE SomeValue NOT IN (SELECT AVal FROM t)
es 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)
Digamos además que AVal es NULL donde ID = 4. Por lo tanto, != la comparación devuelve DESCONOCIDO. La tabla de verdad lógica para AND establece que DESCONOCIDO y VERDADERO es DESCONOCIDO, DESCONOCIDO y FALSO es FALSO. No hay ningún valor al que se le pueda aplicar AND con DESCONOCIDO para producir el resultado VERDADERO
Por lo tanto, si alguna fila de esa subconsulta devuelve NULL, todo el operador NOT IN se evaluará como FALSE o NULL y no se devolverá ningún registro.
Si el planificador de la ejecución dice que son iguales, son iguales. Utilice el que haga que su intención sea más obvia; en este caso, el segundo.