¿Cuál es la diferencia entre NO EXISTE versus NO EN CONTINUACIÓN IZQUIERDA UNIRSE DÓNDE ESTÁ NULO?

151

Me parece que puede hacer lo mismo en una consulta SQL usando NOT EXISTS, NOT IN, o LEFT JOIN WHERE IS NULL. Por ejemplo:

SELECT a FROM table1 WHERE a NOT IN (SELECT a FROM table2)

SELECT a FROM table1 WHERE NOT EXISTS (SELECT * FROM table2 WHERE table1.a = table2.a)

SELECT a FROM table1 LEFT JOIN table2 ON table1.a = table2.a WHERE table1.a IS NULL

No estoy seguro de tener toda la sintaxis correcta, pero estas son las técnicas generales que he visto. ¿Por qué elegiría usar uno sobre el otro? ¿El rendimiento difiere ...? ¿Cuál de estos es el más rápido / más eficiente? (Si depende de la implementación, ¿cuándo usaría cada una?)

froadie
fuente
66
Muchos motores SQL comunes le permiten ver un plan de ejecución. A menudo puede detectar diferencias significativas en la eficiencia para consultas lógicamente equivalentes de esta manera. El éxito de cualquier método depende de factores como el tamaño de la tabla, qué índices están presentes y otros.
Chris Farmer
2
@wich: ninguna base de datos se preocupa por lo que devuelve exactamente dentro de la EXISTScláusula. Puede regresar *, NULLo lo que sea: todo esto se optimizará.
Quassnoi
2
@wich: ¿por qué? Tanto aquí: techonthenet.com/sql/exists.php y aquí: msdn.microsoft.com/en-us/library/ms188336.aspx parecen usar * ...
froadie
8
@wich: no se trata de "expresar interés". Esto se trata de que el analizador de consultas le exija que coloque algo entre SELECTy FROM. Y *es más fácil de escribir. Sí, SQLtiene cierto parecido con un lenguaje natural, pero es analizado y ejecutado por una máquina, una máquina programada. No es que alguna vez irrumpirá repentinamente en su cubículo y gritará "¡deje de exigir los campos adicionales en una EXISTSconsulta porque estoy harto de analizarlos y luego tirarlos!". Está bien con una computadora, de verdad.
Quassnoi
1
@Quassnoi si escribiste código con el único propósito de que una máquina lo interprete, el código se vería horrible, y desafortunadamente algunas personas trabajan así. Sin embargo, si escribe código en otra óptica, escribiendo código para expresar lo que desea que la máquina haga como comunicado a sus pares, escribirá un código mejor y más fácil de mantener. Sea inteligente, escriba código para las personas, no para la computadora.
que

Respuestas:

139

En una palabra:

NOT INes un poco diferente: nunca coincide si solo hay uno NULLen la lista.

  • En MySQL, NOT EXISTSes un poco menos eficiente

  • En SQL Server, LEFT JOIN / IS NULLes menos eficiente

  • En PostgreSQL, NOT INes menos eficiente

  • En Oracle, los tres métodos son iguales.

Quassnoi
fuente
1
Gracias por los enlaces! Y gracias por el resumen rápido ... Mi oficina está bloqueando el enlace por alguna razón: P, pero lo comprobaré tan pronto como llegue a una computadora normal.
froadie
2
Otro punto es que si table1 .acontiene NULLla EXISTSconsulta no devolverá esta fila, pero la NOT INconsulta si table2está vacía. NOT IN vs. NOT EXISTS Nullable Columns: SQL Server
Martin Smith
@MartinSmith: se NULL NOT IN ()evalúa como verdadero (no NULL), tal comoNOT EXISTS (NULL = column)
Quassnoi
2
@Quassnoi - er, Buen punto, entendí eso al revés. El NOT EXISTSsiempre va a devolver la fila, pero NOT INsolamente lo harán si la consulta sub devuelve ninguna fila.
Martin Smith
5

Si la base de datos es buena para optimizar la consulta, los dos primeros se transformarán en algo cercano al tercero.

Para situaciones simples como las de su pregunta, debe haber poca o ninguna diferencia, ya que todas se ejecutarán como uniones. En consultas más complejas, es posible que la base de datos no pueda unir las consultas not iny not exists. En ese caso, las consultas serán mucho más lentas. Por otro lado, una unión también puede funcionar mal si no hay un índice que se pueda usar, por lo que solo porque use una unión no significa que esté seguro. Tendría que examinar el plan de ejecución de la consulta para saber si puede haber algún problema de rendimiento.

Guffa
fuente
2

Suponiendo que está evitando los valores nulos, son todas formas de escribir un anti-join usando Standard SQL.

Una omisión obvia es el equivalente usando EXCEPT:

SELECT a FROM table1
EXCEPT
SELECT a FROM table2

Tenga en cuenta que en Oracle debe usar el MINUSoperador (posiblemente un mejor nombre):

SELECT a FROM table1
MINUS
SELECT a FROM table2

Hablando de sintaxis patentada, también puede haber equivalentes no estándar que vale la pena investigar dependiendo del producto que esté utilizando, por ejemplo, OUTER APPLYen SQL Server (algo así como):

SELECT t1.a
  FROM table1 t1
       OUTER APPLY 
       (
        SELECT t2.a
          FROM table2 t2
         WHERE t2.a = t1.a
       ) AS dt1
 WHERE dt1.a IS NULL;
un día cuando
fuente
0

Cuando necesite insertar datos en la tabla con la clave primaria de múltiples campos, considere que será mucho más rápido (lo intenté en Access pero creo que en cualquier base de datos) para no verificar que "no existen registros con" tales "valores en la tabla", - simplemente inserte en la tabla, y el exceso de registros (por la tecla) no se insertará dos veces

baleks
fuente
0

La perspectiva del rendimiento siempre evita el uso de palabras clave inversas como NOT IN, NOT EXISTS, ... Porque para verificar los elementos inversos, DBMS necesita ejecutar todos los disponibles y descartar la selección inversa.

Lahiru Cooray
fuente
1
¿Y qué propones como solución cuando realmente lo necesitas NOT?
Dnoeth
Bueno, cuando no hay una opción de causa, necesitamos usar operaciones NOT y por eso existen. La mejor práctica es evitarlos cuando tengamos otras soluciones alternativas.
Lahiru Cooray
@onedaywhen, si un optimizador transforma una consulta y devuelve el resultado incorrecto, entonces es un error
David דודו Markovitz
@DuduMarkovitz: sí, y si se contacta con el equipo de SQL Server y ellos reconocen el error, pero se niegan a solucionarlo porque dicen que hacerlo puede hacer que las consultas se ejecuten más lentamente, entonces es un error que debe tratar .
cuando el
@onedaywhen - Este no era un escenario hipotético, supongo :-) ¿Recuerdas por casualidad los detalles del error?
David