Para ilustrar el funcionamiento de la cláusula EXISTS supondremos la existencia de las tablas PRODUCTO y DETALLE que hemos utilizado en anteriores publicaciones. Adicionalmente realizaremos algunos ejemplos para ilustrar el uso de la cláusula EXISTS.
- PRODUCTO { {1,lápiz},{2,creyón},{3,marcador}};
- DETALLE {{1,1,10,2},{2,1,8,1},{3,2,5,3},{4,NULL,5,5},{5,NULL,10,2}}
Algunos ejemplos:
- EXISTS ( SELECT 1 FROM DETALLE WHERE PRODUCTO_ID = 1). Esta instrucción devuelve VERDADERO ya que existe por lo menos un registro en DETALLE que cumple con la condición PRODUCTO_ID = 1.
- EXISTS ( SELECT 1 FROM DETALLE WHERE PRODUCTO_ID = 3). Esta instrucción devuelve FALSO ya que no existe ningún registro en DETALLE que cumpla con la condición PRODUCTO_ID = 3.
- NOT EXISTS ( SELECT 1 FROM DETALLE WHERE PRODUCTO_ID = 1). Esta instrucción devuelve FALSO ya que es la negación del ejemplo [1]. Ya que el EXISTS devuelve VERDADERO, su negación por medio de la cláusula NOT devuelve FALSO.
- EXISTS ( SELECT 1 FROM DETALLE WHERE PRODUCTO_ID = NULL). Esta instrucción devuelve FALSO ya que la comparación PRODUCTO_ID = NULL siempre va a fallar ya que se intenta comparar un valor NULL con el operador de igualdad. Recuerde que para comparar valores NULL debe usarse IS NULL o IS NOT NULL.
- EXISTS ( SELECT 1 FROM DETALLE WHERE PRODUCTO_ID IS NULL). Esta instrucción devuelve VERDADERO ya que existe por lo menos un registro en DETALLE que cumple con la condición PRODUCTO_ID IS NULL.
Explicados los ejemplos, veremos cómo puede ser usada la cláusula EXISTS para validar condiciones de existencia en una consulta anidada. Si esta última no devolviera registros cuyo PRODUCTO_ID fuera NULL, esto bastaría para saber los productos que no se han vendido:
SELECT * FROM PRODUCTO WHERE PRODUCTO_ID NOT IN
(SELECT PRODUCTO_ID FROM DETALLE);
(SELECT PRODUCTO_ID FROM DETALLE);
Según se observó en la publicación previamente mencionada, esta consulta fallaba por la presencia de valores NULL en la consulta anidada. Dicho esto, podemos usar la cláusula EXISTS para saber los productos que no se han vendido de la siguiente manera.
SELECT * FROM PRODUCTO WHERE NOT EXISTS
(SELECT 1 FROM DETALLE WHERE DETALLE.PRODUCTO_ID = PRODUCTO.PRODUCTO_ID );
(SELECT 1 FROM DETALLE WHERE DETALLE.PRODUCTO_ID = PRODUCTO.PRODUCTO_ID );
Para ejecutar dicha consulta, el motor de SQL hará el siguiente recorrido:
- Para cada elemento de la consulta externa, pasará el valor del campo PRODUCTO_ID a la consulta anidada.
- Una vez obtenido el valor de PRODUCTO_ID, la consulta anidada buscará el primer registro que cumpla con la cláusula WHERE. Dependiendo del motor de base de datos, al encontrar la primera coincidencia, la consulta anidada detiene su ejecución y devuelve un conjunto no vacío. Al ser el conjunto no vació la cláusula EXISTS devuelve VERDADERO y la cláusula NOT lo convierte en FALSO. Ello implica que la cláusula WHERE externa falla y el registro no es mostrado. Esto ocurre ya que el producto se ha vendido por lo menos una vez y no debe mostrarse.
- Si la cláusula WHERE de la consulta anidada no encuentra valores coincidentes, ésta devuelve un conjunto vacío, haciendo que la cláusula EXISTS devuelva FALSO y a su vez haciendo que el NOT devuelva VERDADERO. Esto hace que la cláusula WHERE externa sea exitosa y el registro se muestra ya que el producto no se vendió.
- Ya que seguramente la búsqueda de la consulta anidada hace referencia a una clave foránea, esta búsqueda será indexada, por lo que se hará de manera eficiente.
- La consulta original, además de funcionar erróneamente, para cada registro de la consulta externa se revisa secuencialmente cada valor de la consulta anidada. Esto afecta enormemente el rendimiento de la consulta en general.
Finalmente, la cláusula EXISTS puede usarse con claves foráneas compuestas, haciendo referencia dentro de la consulta anidada a cada campo que pertenece a la clave foránea.
Ejemplo:
SELECT .... FROM X WHERE NOT EXISTS
(SELECT 1 FROM Y WHERE Y.A = X.A AND Y.B = X.B AND Y.C=X.C......)
2 comentarios:
Un comentario acerca del ejemplo 3:
El código que muestra este ejemplo es: NOT EXISTS ( SELECT 1 FROM DETALLE WHERE PRODUCTO_ID = 3) y corresponde a la negación del ejemplo 2, en este caso devuelve VERDADERO. Pero el ejemplo 3 se refiere más bien a la negación del ejemplo 1 por lo que el código sería: NOT EXISTS ( SELECT 1 FROM DETALLE WHERE PRODUCTO_ID = 1), como bien explica el artículo, devuelve FALSO.
Efectivamente, hubo un error de mi parte.
Originalmente decía "3" (que ahora aparece tachado) y ahora dice "1" que es lo correcto.
¡Gracias!
Publicar un comentario