Pregunta

¿Hay alguna diferencia en el rendimiento de las siguientes tres declaraciones SQL?

SELECT * FROM tableA WHERE EXISTS (SELECT * FROM tableB WHERE tableA.x = tableB.y)

SELECT * FROM tableA WHERE EXISTS (SELECT y FROM tableB WHERE tableA.x = tableB.y)

SELECT * FROM tableA WHERE EXISTS (SELECT 1 FROM tableB WHERE tableA.x = tableB.y)

Todos deberían trabajar y devolver el mismo conjunto de resultados. ¿Pero importa si SELECT interno selecciona todos los campos de la tabla B, un campo o solo una constante?

¿Existe alguna práctica recomendada cuando todas las afirmaciones se comportan igual?

¿Fue útil?

Solución

La verdad sobre la cláusula EXISTS es que la cláusula SELECT no se evalúa en una cláusula EXISTS; puede intentarlo:

SELECT * 
  FROM tableA 
 WHERE EXISTS (SELECT 1/0 
                 FROM tableB 
                WHERE tableA.x = tableB.y)

... y debería esperar un error de división por cero, pero no lo hará porque no está evaluado. Es por eso que mi hábito es especificar NULL en un EXISTS para demostrar que se puede ignorar el SELECT:

SELECT * 
  FROM tableA 
 WHERE EXISTS (SELECT NULL
                 FROM tableB 
                WHERE tableA.x = tableB.y)

Todo lo que importa en una cláusula EXISTS es las cláusulas FROM y posteriores: WHERE, GROUP BY, HAVING, etc.

Esta pregunta no estaba marcada con una base de datos en mente, y debería ser porque los proveedores manejan las cosas de manera diferente, así que haga una prueba y verifique los planes de explicación / ejecución para confirmar. Es posible que el comportamiento cambie entre versiones ...

Otros consejos

Definitivamente # 1. Se ve " " da miedo, pero date cuenta que el optimizador hará lo correcto y es expresivo de intención. También hay una pequeña bonificación de error tipográfico si uno piensa accidentalmente EXISTS pero escribe IN. # 2 es aceptable pero no expresivo. La tercera opción apesta en mi opinión no tan humilde. Es demasiado parecido a decir " si 'no hay valor' existe " para mayor comodidad.

En general, es importante no tener miedo de escribir código que parezca ineficiente si proporciona otros beneficios y no afecta el rendimiento.

Es decir, el optimizador casi siempre ejecutará su complicada herramienta de unión / selección / agrupación para guardar una simple EXISTA / subconsulta de la misma manera.

Después de haberte dado kudos para reescribir inteligentemente esa desagradable O de una unión, eventualmente Tenga en cuenta que el optimizador todavía usaba el mismo plan de ejecución de mierda para resolver la consulta, mucho más fácil de entender, con O incrustado de todos modos.

La moraleja de la historia es conocer el optimizador de su plataforma. Pruebe diferentes cosas y vea lo que realmente se está haciendo porque los supuestos escalofríos de la rodilla con respecto a la optimización de consultas 'decorativas' casi siempre son incorrectos e irrelevantes desde mi experiencia.

Me doy cuenta de que esta es una publicación antigua, pero me pareció importante agregar claridad sobre por qué uno podría elegir un formato en lugar de otro.

Primero, como han señalado otros, el motor de la base de datos es upuesto para ignorar la cláusula de selección. Cada versión de SQL Server tiene / lo hace, Oracle lo hace, MySQL lo hace y así sucesivamente. En muchas, muchas lunas de desarrollo de bases de datos, solo he encontrado un DBMS que no ignoró correctamente la cláusula de selección: Microsoft Access. Específicamente, versiones anteriores de MS Access (no puedo hablar con las versiones actuales).

Antes de descubrir esta función " " ;, solía usar Exists (Select * ... . Sin embargo, descubrí que MS Access se transmitiría a través de cada columna de la subconsulta y luego descártelos ( Select 1/0 tampoco funcionaría). Eso me convenció de cambiar a Select 1 . Si incluso un DBMS era estúpido, podría existir otro.

La escritura de existe (la selección 1 ... es lo suficientemente clara para expresar la intención de transportación (es francamente ridículo afirmar que "es demasiado parecido a decir" si no existe un "valor" para mayor comodidad . ") y hace que las probabilidades de que un DBMS haga algo estúpido con la declaración Select sea casi imposible. Select Null tendría el mismo propósito pero es simplemente más caracteres para escribir.

Cambié a Exists (Seleccione 1 para estar absolutamente seguro de que el DBMS no podría ser estúpido. Sin embargo, eso fue hace muchas lunas, y hoy espero que la mayoría de los desarrolladores esperen ver Existe (Seleccione * que funcionará exactamente igual.

Dicho esto, puedo proporcionar una buena razón para evitar que exista (seleccione * , incluso si su DBMS lo evalúa correctamente. Es mucho más fácil encontrar y eliminar todos los usos de Select * si no tiene que omitir cada instancia de su uso en una cláusula Exists.

Al menos en SQL Server,

La cantidad más pequeña de datos que se pueden leer del disco es una sola página " " de espacio en disco. Tan pronto como el procesador lee un registro que satisface los predicados de la subconsulta, puede detenerse. La subconsulta no se ejecuta como si fuera propia, y luego se incluye en la consulta externa, se ejecuta como parte del plan de consulta completo para todo el asunto. Entonces, cuando se usa como una subconsulta, realmente no importa lo que está en la cláusula Select, no se devuelve nada " a la consulta externa de todos modos, excepto un booleano para indicar si se encontró un solo registro o no ...

Los tres usan exactamente el mismo plan de ejecución

Siempre uso [Seleccionar * De ...] porque creo que se lee mejor, al no implicar que quiero que me devuelvan algo de la subconsulta.

EDITAR: De dave costa comment ... Oracle también usa el mismo plan de ejecución para las tres opciones

Esta es una de esas preguntas que está al borde de iniciar algún tipo de guerra santa.

Existe una discusión bastante buena al respecto aquí .

Creo que la respuesta es probablemente usar la tercera opción, pero el aumento de velocidad es tan infinitesimal que no merece la pena preocuparse. Es fácil el tipo de consulta que SQL Server puede optimizar internamente de todos modos, por lo que puede encontrar que todas las opciones son equivalentes.

El EXISTS devuelve un boolean que no es información real, y dice que la mejor práctica es usar el # 3.

Plan de ejecución . Apréndelo, úsalo, ámalo

No hay forma de adivinar, realmente.

Además de lo que otros han dicho, la práctica de usar SELECT 1 se originó en el antiguo Microsoft SQL Server (antes de 2005). tabla para SELECT * . Ningún otro DBMS, que yo sepa, tiene esta deficiencia.

El EXISTS prueba la existencia de filas, no lo que hay en ellas, por lo que, aparte de algunas características del optimizador similares a las anteriores, realmente no importa lo que esté en la lista SELECT.

El SELECT * parece ser el más habitual, pero otros también son aceptables.

# 3 Debería ser el mejor, ya que de todos modos no necesita los datos devueltos. Traer los campos solo agregará una sobrecarga adicional

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top