Esta es la segunda de dos partes de cláusulas SQL.  Juan fue el autor de la  primera donde trató el tema de IN(). Ahora les hablaré sobre otro tema un poco parecido, EXISTS.

Como Juan demostró IN() puede ser muy útil para muchas situaciones cuando queremos registros de una tabla que coincidan con registros de otra sin la necesidad de utilizar un JOIN. En algunos casos, una sub-consulta que utiliza un JOIN puede ser un poco más complicado y por tanto puede tomar mucho más procesamiento al ejecutarse. Podemos simplificar un poco todo esto utilizando EXISTS.

La diferencia entre IN() y EXISTS() es que IN() retorna un grupo de registros como resultado el cual evaluado por la consulta principal. EXISTS() retorna un valor booleano sin devolver ningún dato de la sub-consulta:

SELECT *
FROM tblOrders
WHERE CustomerID IN (
SELECT CustomerID
FROM tblCustomers
WHERE State = 'IL'
);

Supongamos que tenemos tres clientes en el estado de Illinois. El IN() puede ser equivalente a esto:

SELECT *
FROM tblOrders
WHERE CustomerID IN (4, 8, 15);

Como pueden ver, la sub-consulta y retorna una lista de posibles valores los cuales son evaluados por la consulta exterior para determinar si la orden va a retornar un registro o no.
Veamos cómo podemos expresar eso mismo utilizando EXISTS.

 

SELECT *
FROM tblOrders
WHERE EXISTS (
SELECT NULL
FROM tblCustomers
WHERE tblOrders.CustomerID = tblCustomers.CustomerID
AND tblCustomers.State = 'IL'
);

Hay un sinnúmero de diferencias. Vamos a repasarla una por una.
Primero, usted tiene que darse cuenta que no dijimos “CustomerID = EXISTS(…)”. EXISTS() por sí mismo retorna un valor booleano, por lo tanto no necesita ser validado con ninguna otra columna y por esta razón es un estándar para ser utilizado sin ninguna comparación.
Segundo, nosotros relacionamos la consulta por tblOrders.CustomerID con su consulta exterior en tblCustomers.CustomerID. Algunos se estarán preguntando, “¿Pero correlacionar las subconsultas no está mal?” no siempre es el caso. Por ejemplo, SQL Server tiende a tratar a un EXISTS como un “semi-join” y por lo tanto es más eficiente.
Tercero, nosotros aplicamos el mismo criterio que usamos en el ejemplo original para filtrar solo los clientes que están en el estado de Illinois. Por lo tanto todo cliente que su ID sea 3 y este en el estado de CA la subcosulta lo evaluara lógicamente como cero resultados lo cual hará que la cláusula EXISTS() retorne false.
Por último, el contenido de la subconsulta es totalmente irrelevante. Escribí “SELECT NULL” porque no hay nada para ser evaluado en el SELECT. Si usted no está convencido, entonces puede probar esto:
... WHERE EXISTS (
SELECT 1 / 0
FROM tblCustomers
);

¿Seguramente la división por cero puede provocar que la consulta falle con algún error, cierto? No. Solo ejecuta la consulta ignorando el SELECT, encuentra los registros en tblCustomers y por lo tanto retorna true. Esto nos ahorra un paso comparado con el IN() el cual requiere evaluar el resultado. Lo único que le importa al EXISTS es si hay o no hay registros que coincidan como resultado de la subconsulta. Esa es la razón por lo que tenemos 2 criterios coincidentes en el CustomerID con el tblOders.CustomerID aparte de los criterios de estado.

Entonces, ¿Cuál es más rápido?

Generalmente hablando, JOINS puede ser lo primero que te llegue a la mente a la hora de hacer una consulta. Nosotros podemos considerar el uso de IN() o EXISTS si queremos comparar datos de tablas diferentes sin tener que utilizar un JOIN que nos cambie el resultado. Pero eso tiende a depender más en la estructura y el contenido de la consulta— algunas veces IN()  tiene un mejor rendimiento porque tiene la capacidad de evaluar toda la subconsulta una vez y después utilizarla en un segundo paso. Otras veces EXISTS() es mejor porque no tiene resultados que evaluar. Para consultas pequeñas, usted no vera una diferencia significativa comparando los métodos, pero para consultas más grandes o donde se ejecuten constantemente, usted puede verse en la necesidad de hacer un punto de referencia y ver cuál es mejor para el trabajo. Además diferentes motores de base de datos tienen diferente “preferencias”  — Considerando estos dos enfoques diferentes, podemos utilizar la siguiente consulta solo para mostrar órdenes que no tienen clientes asociados con la orden:

Fustrar un Join:
SELECT *
FROM tblOrders
LEFT JOIN tblCustomers
ON tblOrders.CustomerID = tblCustomers.CustomerID
WHERE tblCustomers.CustomerID IS NULL;

Noten como el join es bloqueado o “forzado” por el predicado “tblCustomers.CustomerID IS NULL”, el cual restringe el resultado para mostrar solo las órdenes que no tienen clientes.

Existence check:
SELECT *
FROM tblOrders
WHERE NOT EXISTS (
SELECT NULL
FROM tblCustomers
WHERE tblCustomers.CustomerID = tblOrders.CustomerID
);

Igualmente, Nosotros relacionamos el customerID entre las dos tablas utilizando la cláusula EXISTS, pero no comparamos el resultado que se emite.
Ambo enfoques tendrán el mismo resultado — Por eso, obtenemos una lista de órdenes que no tienen clientes asociados. En el motor de base de datos de Access es preferible frustrar o bloquear los JOIN pero en SQL Server es mejor utilizar EXISTS. Cada motor de base de datos también tiene sus propias “preferencias” así que para un mejor rendimiento de las consulta es esencial para usted usar diferente expresiones de SQL y ver cuál es la que mejor trabaja en su consulta. Como mencioné anteriormente, la estructura y el contenido de la consulta puede influir mucho en el rendimiento, así que esto debe de ser lo primero a tomar en cuenta. Pero cuando usted tiene que trabajar con el esquema sin modificarlo es muy útil reconocer los patrones de sentencias comunes de SQL para cuando se encuentre con problemas similares, ahora usted tiene más herramientas a su disposición para conseguir más soluciones incluso para las consultas más complicadas.

¡Sea Feliz!