Rendimiento de SQL Server IN frente a EXISTS

115

Tengo curiosidad por saber cuál de las siguientes opciones sería más eficiente.

Siempre he sido un poco cauteloso al usarlo INporque creo que SQL Server convierte el conjunto de resultados en una gran IFdeclaración. Para un conjunto de resultados grande, esto podría resultar en un rendimiento deficiente. Para conjuntos de resultados pequeños, no estoy seguro de que sea preferible ninguno. Para conjuntos de resultados grandes, ¿no EXISTSsería más eficiente?

WHERE EXISTS (SELECT * FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

vs.

WHERE bx.BoxID IN (SELECT BoxID FROM Base WHERE [Rank = 2])
Randy Minder
fuente
8
La mejor manera de averiguarlo es probarlo y hacer algunas mediciones.
Klaus Byskov Pedersen
10
Hay tiene que ser un tropecientos duplicados de este ......
marc_s
5
@marc_s - Probablemente sí, pero en el tiempo que me hubiera llevado revisar todas las publicaciones sobre este tema y encontrar una que se ajustara a mi caso, tenía cuatro respuestas a mi pregunta.
Randy Minder
7
Para su información si usted está queriendo la mayoría manera performante, puede select 1 from Base...en su where existspuesto que en realidad no se preocupan por los resultados, sólo que en realidad existe una fila.
brad
2
@marc_s eso es realmente triste, porque me tomé el tiempo de revisar las publicaciones para no agregar más basura a stackoverflow. No necesito una respuesta personalizada para hacer mi trabajo. Ese es el tipo de pensamiento que agregó un millón de duplicados en lugar de solo unos pocos con buenas respuestas
IvoC

Respuestas:

140

EXISTS será más rápido porque una vez que el motor haya encontrado un golpe, dejará de mirar ya que la condición ha demostrado ser cierta.

Con IN, recopilará todos los resultados de la subconsulta antes de continuar con el procesamiento.

keithwarren7
fuente
4
Ese es un buen punto. La declaración IN requiere que SQL Server genere un conjunto de resultados completo y luego cree una declaración IF grande, creo.
Randy Minder
72
Esto solía ser cierto, pero en las versiones actuales (al menos 2008) el optimizador es mucho más inteligente ... en realidad trata IN () como un EXISTS ().
Aaron Bertrand
11
@ Aaron: sí, normalmente el optimizador producirá internamente un plan mejor. Sin embargo, depender de atajos internos podría ser perjudicial en escenarios más complejos.
Scott Coates
2
Esto es simplemente incorrecto. Fue en 2010 y todavía lo es.
Magnus
2
IN y EXISTS tienen exactamente el mismo plan de consulta e IO. No hay razón para pensar que su desempeño es diferente. verifique sus estadísticas de tiempo y compruébelo usted mismo
Nelssen
40

La respuesta aceptada es miope y la pregunta un poco suelta en eso:

1) Tampoco mencione explícitamente si un índice de cobertura está presente en el lado izquierdo, derecho o en ambos lados.

2) Tampoco tiene en cuenta el tamaño del conjunto del lado izquierdo de entrada y del conjunto del lado derecho de entrada.
(La pregunta solo menciona un gran conjunto de resultados en general ).

Creo que el optimizador es lo suficientemente inteligente como para convertir entre "en" y "existe" cuando hay una diferencia de costo significativa debido a (1) y (2); de lo contrario, puede usarse solo como una sugerencia (por ejemplo, existe para alentar el uso de un índice de búsqueda en el lado derecho).

Ambos formularios se pueden convertir para unir formularios internamente, invertir el orden de combinación y ejecutar como bucle, hash o combinación, en función de los recuentos de filas estimados (izquierda y derecha) y la existencia del índice en los lados izquierdo, derecho o ambos.

crokusek
fuente
3
No sé por qué esta excelente respuesta no ha recibido más atención. Entender el índice / estructura para ambos lados podría afectar, estoy de acuerdo. Bien dicho.
SheldonH
El optimizador siempre da el mismo plan para INy EXISTS. Intente idear cualquier caso en el que no obtengan el mismo plan (aunque esto no se aplica a NOT INy NOT EXISTS)
Martin Smith
@MartinSmith Supongo que sabes de lo que estás hablando, pero ¿tienes alguna prueba de que los planes son siempre los mismos? Si es así, aclararía el desacuerdo de una década aquí.
MarredCheese
@MarredCheese - la responsabilidad recae en las personas que afirman que es diferente producir un solo ejemplo de esto
Martin Smith
37

Hice algunas pruebas en SQL Server 2005 y 2008, y tanto en EXISTS como en IN regresaron con exactamente el mismo plan de ejecución real, como han dicho otros. El Optimizador es óptimo. :)

Sin embargo, algo a tener en cuenta, EXISTS, IN y JOIN a veces pueden devolver resultados diferentes si no formula su consulta correctamente: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210 .aspx

Adam Nofsinger
fuente
5

Aquí hay muchas respuestas engañosas, incluida la muy votada (aunque no creo que sus operaciones hayan significado daño). La respuesta corta es: son iguales.

Hay muchas palabras clave en el lenguaje (T-) SQL, pero al final, lo único que realmente sucede en el hardware son las operaciones como se ve en el plan de consulta de ejecución.

La operación relacional (teoría matemática) que hacemos cuando invocamos [NOT] INy [NOT] EXISTSes la semi-unión (anti-unión cuando se usa NOT). No es una coincidencia que las correspondientes operaciones del servidor SQL tengan el mismo nombre . No hay operación que mencione INni en EXISTSningún lugar - solo (anti) semiuniones. Por lo tanto, no hay forma de que una opción INfrente a una lógica equivalente EXISTSpueda afectar el rendimiento porque hay una y única forma, la operación de ejecución (anti) semiunión, de obtener sus resultados .

Un ejemplo:

Consulta 1 ( plan )

select * from dt where dt.customer in (select c.code from customer c where c.active=0)

Consulta 2 ( plan )

select * from dt where exists (select 1 from customer c where c.code=dt.customer and c.active=0)
George Menoutis
fuente
¿Lo has probado? Si es así, ¿puede compartir su SQL y sus resultados?
UnhandledExcepSean
Lo probé varias veces. Puedo crear otro caso de prueba, y lo haré, pero un caso de prueba no significa que el optimizador hará exactamente el mismo plan en tablas con diferentes estadísticas. Esto podría llevar a alguien a pensar que la respuesta es parcial, pero la inexistencia de múltiples operadores de semiunión es un hecho. Quizás encuentre una lista en alguna parte y la vincule.
George Menoutis
5

Iría con EXISTS sobre IN, vea el enlace a continuación:

SQL Server: JOIN vs IN vs EXISTS - la diferencia lógica

Existe la idea errónea de que IN se comporta de la misma manera que EXISTS o JOIN en términos de resultados devueltos. Esto simplemente no es cierto.

IN: Devuelve verdadero si un valor especificado coincide con cualquier valor en una subconsulta o una lista.

Existe: devuelve verdadero si una subconsulta contiene filas.

Unir: une 2 conjuntos de resultados en la columna de unión.

Crédito del blog: https://stackoverflow.com/users/31345/mladen-prajdic

Curtidor
fuente
Vaya, gracias por tu blog y tu explicación.
Christian Müller
3

Los planes de ejecución suelen ser idénticos en estos casos, pero hasta que no vea cómo el optimizador tiene en cuenta todos los demás aspectos de los índices, etc., nunca lo sabrá.

Cade Roux
fuente
3

Entonces, IN no es lo mismo que EXISTS ni producirá el mismo plan de ejecución.

Por lo general, EXISTS se usa en una subconsulta correlacionada, lo que significa que UNIRÁ la consulta interna EXISTS con su consulta externa. Eso agregará más pasos para producir un resultado, ya que necesita resolver las uniones de consultas externas y las uniones de consultas internas luego coinciden con sus cláusulas where para unir ambas.

Por lo general, IN se usa sin correlacionar la consulta interna con la consulta externa, y eso se puede resolver en un solo paso (en el mejor de los casos).

Considera esto:

  1. Si usa IN y el resultado de la consulta interna son millones de filas de valores distintos, probablemente funcionará MÁS LENTO que EXISTS dado que la consulta EXISTS funciona correctamente (tiene los índices correctos para unirse con la consulta externa).

  2. Si usa EXISTS y la combinación con su consulta externa es compleja (lleva más tiempo realizarla, no hay índices adecuados), la consulta se ralentizará según el número de filas en la tabla externa; a veces, el tiempo estimado para completar puede ser en días. Si el número de filas es aceptable para su hardware dado, o la cardinalidad de los datos es correcta (por ejemplo, menos valores DISTINCT en un conjunto de datos grande), IN puede funcionar más rápido que EXISTS.

  3. Todo lo anterior se notará cuando tenga una buena cantidad de filas en cada tabla (por justo me refiero a algo que excede el procesamiento de su CPU y / o los umbrales de RAM para el almacenamiento en caché).

Entonces la RESPUESTA es DEPENDE. Puede escribir una consulta compleja dentro de IN o EXISTS, pero como regla general, debe intentar usar IN con un conjunto limitado de valores distintos y EXISTS cuando tenga muchas filas con muchos valores distintos.

El truco consiste en limitar el número de filas a escanear.

Saludos,

MarianoC

MarianoC
fuente
1

Para optimizar el EXISTS, sea muy literal; algo tiene que estar ahí, pero en realidad no necesita ningún dato devuelto por la subconsulta correlacionada. Solo está evaluando una condición booleana.

Entonces:

WHERE EXISTS (SELECT TOP 1 1 FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

Debido a que la subconsulta correlacionada es RBAR, el primer resultado obtenido hace que la condición sea verdadera y no se procesa más.

Josh Lewis
fuente
Siempre sería extremadamente cauteloso al usar la codificación LEFT JOIN + NULL, porque es muy fácil obtener resultados perdidos o sesgados si no tiene mucho cuidado en su manejo NULL. Rara vez he encontrado una situación en la que EXISTS o un CTE (para encontrar duplicación o inserción sintética para datos faltantes), no cumplen con los mismos requisitos y superan a LEFT JOIN + NULL
Josh Lewis
3
TOP 1 debe ser completamente extraño (o evento redundante) cuando se usa con EXISTS. EXISTS siempre regresa tan pronto encuentra una fila coincidente.
Karl Kieninger
Hasta ahora, no vi ningún beneficio de rendimiento con este enfoque.
Muestre
-1

Fuera de mi cabeza y no se garantiza que sea correcto: creo que el segundo será más rápido en este caso.

  1. En el primero, la subconsulta correlacionada probablemente hará que la subconsulta se ejecute para cada fila.
  2. En el segundo ejemplo, la subconsulta solo debería ejecutarse una vez, ya que no está correlacionada.
  3. En el segundo ejemplo, se INproducirá un cortocircuito tan pronto como encuentre una coincidencia.
RedFilter
fuente