Siempre que necesito verificar la existencia de alguna fila en una tabla, tiendo a escribir siempre una condición como:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT * -- This is what I normally write
FROM another_table
WHERE another_table.b = a_table.b
)
Algunas otras personas lo escriben como:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT 1 --- This nice '1' is what I have seen other people use
FROM another_table
WHERE another_table.b = a_table.b
)
Cuando la condición es en NOT EXISTS
lugar de EXISTS
: En algunas ocasiones, podría escribirlo con una LEFT JOIN
y una condición adicional (a veces llamada antiunión ):
SELECT a, b, c
FROM a_table
LEFT JOIN another_table ON another_table.b = a_table.b
WHERE another_table.primary_key IS NULL
Intento evitarlo porque creo que el significado es menos claro, especialmente cuando lo que es tuyo primary_key
no es tan obvio, o cuando tu clave principal o tu condición de unión es de varias columnas (y puedes olvidar fácilmente una de las columnas). Sin embargo, a veces mantienes el código escrito por otra persona ... y está ahí.
¿Hay alguna diferencia (aparte del estilo) para usar en
SELECT 1
lugar deSELECT *
?
¿Hay algún caso de esquina donde no se comporta de la misma manera?Aunque lo que escribí es SQL estándar (AFAIK): ¿Hay tanta diferencia para diferentes bases de datos / versiones anteriores?
¿Hay alguna ventaja en la explicidad de escribir un antijoin?
¿Los planificadores / optimizadores contemporáneos lo tratan de manera diferente a laNOT EXISTS
cláusula?
fuente
EXISTS (SELECT FROM ...)
.Respuestas:
No, no hay diferencia en la eficiencia entre
(NOT) EXISTS (SELECT 1 ...)
y(NOT) EXISTS (SELECT * ...)
en todos los DBMS principales. A menudo he visto(NOT) EXISTS (SELECT NULL ...)
ser utilizado también.En algunos incluso puede escribir
(NOT) EXISTS (SELECT 1/0 ...)
y el resultado es el mismo, sin ningún error (división por cero), lo que demuestra que la expresión allí ni siquiera se evalúa.Sobre el
LEFT JOIN / IS NULL
método antijoin, una corrección: esto es equivalente aNOT EXISTS (SELECT ...)
.En este caso,
NOT EXISTS
vsLEFT JOIN / IS NULL
, puede obtener diferentes planes de ejecución. En MySQL, por ejemplo, y principalmente en versiones anteriores (anteriores a 5.7), los planes serían bastante similares pero no idénticos. Los optimizadores de otros DBMS (SQL Server, Oracle, Postgres, DB2) son, hasta donde yo sé, más o menos capaces de reescribir estos 2 métodos y considerar los mismos planes para ambos. Aún así, no existe tal garantía y al hacer la optimización, es bueno verificar los planes de diferentes reescrituras equivalentes, ya que podría haber casos en los que cada optimizador no reescribe (por ejemplo, consultas complejas, con muchas combinaciones y / o tablas derivadas / subconsultas dentro de la subconsulta, donde las condiciones de varias tablas, columnas compuestas utilizadas en las condiciones de unión) o las opciones y planes del optimizador se ven afectados de manera diferente por los índices, configuraciones, etc. disponiblesTambién tenga en cuenta que
USING
no se puede usar en todos los DBMS (SQL Server, por ejemplo). El más comúnJOIN ... ON
funciona en todas partes.Y las columnas deben tener el prefijo con el nombre / alias de la tabla
SELECT
para evitar errores / ambigüedades cuando tenemos uniones.Por lo general, también prefiero poner la columna unida en el
IS NULL
cheque (aunque el PK o cualquier columna no anulable estaría bien, podría ser útil para la eficiencia cuando el planLEFT JOIN
utiliza un índice no agrupado):También hay un tercer método para antijoins,
NOT IN
pero tiene una semántica diferente (¡y resultados!) Si la columna de la tabla interna es anulable. Sin embargo, puede usarse excluyendo las filas conNULL
, haciendo que la consulta sea equivalente a las 2 versiones anteriores:Esto también suele generar planes similares en la mayoría de los DBMS.
fuente
[NOT] IN (SELECT ...)
, aunque equivalentes, se desempeñaron muy mal. ¡Evítalo!SELECT *
Ciertamente está haciendo más trabajo. Yo, por simplicidad, aconsejaría usarSELECT 1
Hay una categoría de casos donde
SELECT 1
ySELECT *
no son intercambiables, más específicamente, uno siempre será aceptado en esos casos, mientras que el otro no lo será.Estoy hablando de casos en los que necesita verificar la existencia de filas de un conjunto agrupado . Si la tabla
T
tiene columnasC1
yC2
está verificando la existencia de grupos de filas que coincidan con una condición específica, puede usarSELECT 1
así:pero no puedes usar
SELECT *
de la misma manera.Eso es simplemente un aspecto sintáctico. Cuando ambas opciones se aceptan sintácticamente, lo más probable es que no tenga diferencias en términos de rendimiento o de resultados, como se explicó en la otra respuesta .
Notas adicionales después de los comentarios
Parece que no muchos productos de bases de datos realmente admiten esta distinción. Los productos como SQL Server, Oracle, MySQL y SQLite aceptarán
SELECT *
con gusto en la consulta anterior sin ningún error, lo que probablemente significa que tratan a los EXISTENTESSELECT
de una manera especial.PostgreSQL es un RDBMS donde
SELECT *
puede fallar, pero aún puede funcionar en algunos casos. En particular, si está agrupando por PK,SELECT *
funcionará bien, de lo contrario fallará con el mensaje:fuente
GROUP BY
, el concepto de no*
tiene sentido (o, al menos, no está tan claro).Una forma posiblemente interesante de reescribir la
EXISTS
cláusula que resulta en una consulta más limpia y quizás menos engañosa, al menos en SQL Server sería:La versión anti-semi-join de eso se vería así:
Ambos están típicamente optimizados para el mismo plan que
WHERE EXISTS
oWHERE NOT EXISTS
, pero la intención es inconfundible y no tiene "extraño"1
o*
.Curiosamente, los problemas de verificación nula asociados con
NOT IN (...)
son problemáticos<> ALL (...)
, mientras que elNOT EXISTS (...)
no sufre ese problema. Considere las siguientes dos tablas con una columna anulable:Agregaremos algunos datos a ambos, con algunas filas que coinciden y algunas que no:
La
NOT IN (...)
consulta:Tiene el siguiente plan:
La consulta no devuelve filas ya que los valores NULL hacen que la igualdad sea imposible de confirmar.
Esta consulta, con
<> ALL (...)
muestra el mismo plan y no devuelve filas:La variante que usa
NOT EXISTS (...)
muestra una forma de plan ligeramente diferente y devuelve filas:El plan:
Los resultados de esa consulta:
Esto hace que el uso sea
<> ALL (...)
tan propenso a resultados problemáticos comoNOT IN (...)
.fuente
*
extraño: leoEXISTS (SELECT * FROM t WHERE ...)
ASthere is a _row_ in table _t_ that...
. De todos modos, me gusta tener alternativas, y la suya es claramente legible. Una duda / advertencia: ¿cómo se comportará sib
es anulable? [He tenido malas experiencias y algunas noches cortas cuando trato de descubrir un error causado por unx IN (SELECT something_nullable FROM a_table)
]La "prueba" de que son idénticos (en MySQL) es hacer
luego repite con
SELECT 1
. En ambos casos, la salida 'extendida' muestra que se transformó enSELECT 1
.Del mismo modo,
COUNT(*)
se convierte enCOUNT(0)
.Otra cosa a tener en cuenta: se han realizado mejoras de optimización en versiones recientes. Puede valer la pena comparar
EXISTS
vs anti-une. Su versión puede hacer un mejor trabajo con uno versus el otro.fuente
En algunas bases de datos, esta optimización aún no funciona. Como por ejemplo en PostgreSQL A partir de la versión 9.6, esto fallará.
Y esto tendrá éxito.
Está fallando porque lo siguiente falla pero eso todavía significa que hay una diferencia.
Puede encontrar más información sobre este capricho particular y la violación de la especificación en mi respuesta a la pregunta: ¿La especificación SQL requiere un GROUP BY en EXISTS ()
fuente
Siempre he usado
select top 1 'x'
(SQL Server)Teóricamente,
select top 1 'x'
sería más eficiente queselect *
, ya que el primero estaría completo después de seleccionar una constante sobre la existencia de una fila de clasificación, mientras que el segundo seleccionaría todo.SIN EMBARGO, aunque desde el principio puede haber sido relevante, la optimización ha hecho que la diferencia sea irrelevante en probablemente todos los RDBS principales.
fuente
top n
sinorder by
una buena idea.select top 1 'x'
no debería ser más eficiente queselect *
en unaExist
expresión. Prácticamente puede ser más eficiente si el optimizador funciona por debajo de lo óptimo, pero en teoría ambas expresiones son equivalentes.IF EXISTS(SELECT TOP(1) 1 FROM
es un hábito mejor a largo plazo y en todas las plataformas simplemente porque ni siquiera necesita comenzar a preocuparse acerca de cuán buena o mala es su plataforma / versión actual; y SQL se está moviendo deTOP n
hacia parametrizableTOP(n)
. Esto debería ser una habilidad de aprender una vez.fuente
TOP
Ni siquiera es válido SQL.TOP (n)
en "SQL", el lenguaje de consulta estándar. Hay uno en T-SQL que es el dialecto que utiliza Microsoft SQL Server.