NO EN vs NO EXISTE

Resuelto ilitirit asked hace 16 años • 11 respuestas

¿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.

ilitirit avatar Oct 06 '08 09:10 ilitirit
Aceptado

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 NULLs, la NOT INversión necesitará hacer más trabajo (incluso si en NULLrealidad no hay s presentes en los datos) y la semántica de NOT INsi NULLlos s están presentes Es poco probable que sean los que desea de todos modos.

Cuando ninguno de los dos Products.ProductIDo lo [Order Details].ProductIDpermite , se tratará de manera idéntica a la siguiente consulta.NULLNOT 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.

Ninguno NULL

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].ProductIDes 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 ProductIds, es no devolver resultados. Consulte el carrete adicional anti semi unión y recuento de filas para verificar que se agregue al plan.

Uno NULO

Si Products.ProductIDtambié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.ProductIdno debe devolverse en los resultados, excepto si la NOT INsubconsulta 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.

Ambos NULL

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 NULLpueda 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 NULLfilas 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 INcolumna NULLcapaz. Este artículo muestra otro para una consulta a la AdventureWorks2008base de datos.

Para NOT INuna NOT NULLcolumna o NOT EXISTScontra una columna que acepta valores NULL o no, proporciona el siguiente plan.

No existe

Cuando la columna cambia a NULL-able, el NOT INplan ahora se ve así

No en - nulo

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.SalesOrderDetailno contiene ningún NULL ProductIDcorreo electrónico, duplicará el número de operaciones de búsqueda requeridas.

Martin Smith avatar Jun 17 '2012 20:06 Martin Smith

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.

buckley avatar May 09 '2012 12:05 buckley

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.

John Millikin avatar Oct 06 '2008 02:10 John Millikin