Usar subconsultas independientes o correlacionadas

Completado

Anteriormente, examinamos las subconsultas independientes en las que la consulta interna es independiente de la consulta externa, se ejecuta una vez y devuelve sus resultados a la consulta externa. T-SQL también admite las subconsultas correlacionadas, en las que la consulta interna hace referencia a la columna de la consulta externa y conceptualmente se ejecuta una vez por fila.

Trabajar con subconsultas correlacionadas

Al igual que las subconsultas independientes, las subconsultas correlacionadas son instrucciones SELECT anidadas dentro de una consulta externa. Las subconsultas correlacionadas también pueden ser subconsultas escalares o multivalor. Normalmente se usan cuando la consulta interna necesita hacer referencia a un valor en la consulta externa.

Sin embargo, a diferencia de las subconsultas independientes, hay algunas consideraciones especiales cuando se usan subconsultas correlacionadas:

  • Las subconsultas correlacionadas no se pueden ejecutar por separado desde la consulta externa. Esta restricción complica las pruebas y la depuración.
  • A diferencia de las subconsultas independientes, que se procesan una vez, las subconsultas correlacionadas se ejecutarán varias veces. Lógicamente, la consulta externa se ejecuta primero y, para cada fila devuelta, se procesa la consulta interna.

En el ejemplo siguiente se usa una subconsulta correlacionada para devolver el pedido más reciente de cada cliente. La subconsulta hace referencia a la consulta externa y hace referencia a su valor CustomerID en su cláusula WHERE. Para cada fila de la consulta externa, la subconsulta busca el identificador de pedido máximo del cliente al que se hace referencia en esa fila y la consulta externa comprueba si la fila que se está analizando es la fila con ese identificador de pedido.

SELECT SalesOrderID, CustomerID, OrderDate
FROM SalesLT.SalesOrderHeader AS o1
WHERE SalesOrderID =
    (SELECT MAX(SalesOrderID)
     FROM SalesLT.SalesOrderHeader AS o2
     WHERE o2.CustomerID = o1.CustomerID)
ORDER BY CustomerID, OrderDate;

Escritura de subconsultas correlacionadas

Para escribir subconsultas correlacionadas, tenga en cuenta las siguientes directrices:

  • Escriba la consulta externa para aceptar el resultado devuelto adecuado de la consulta interna. Si la consulta interna es escalar, puede usar operadores de igualdad y comparación, como =, <, > y <>, en la cláusula WHERE. Si la consulta interna puede devolver varios valores, use un predicado IN. Cree un plan para controlar los resultados NULL.
  • Identifique la columna de la consulta externa a la que hará referencia la subconsulta correlacionada. Declare un alias para la tabla que es el origen de la columna en la consulta externa.
  • Identifique la columna de la tabla interna que se comparará con la columna de la tabla externa. Cree un alias para la tabla de origen, como hizo para la consulta externa.
  • Escriba la consulta interna para recuperar valores de su origen, en función del valor de entrada de la consulta externa. Por ejemplo, use la columna externa en la cláusula WHERE de la consulta interna.

La correlación entre las consultas interna y externa se produce cuando la consulta interna hace referencia al valor externo para su comparación. Es esta correlación la que proporciona a la subconsulta su nombre.

Trabajar con EXISTS

Además de recuperar valores de una subconsulta, T-SQL proporciona un mecanismo para comprobar si se devolverían resultados de una consulta. El predicado EXISTS determina si existen filas que cumplan una condición especificada, pero en lugar de devolverlas, devuelve TRUE o FALSE. Esta técnica es útil para validar los datos sin incurrir en la sobrecarga de recuperar y procesar los resultados.

Cuando una subconsulta está relacionada con la consulta externa mediante el predicado EXISTS, SQL Server controla los resultados de la subconsulta de una manera especial. En lugar de recuperar un valor escalar o una lista multivalor de la subconsulta, EXISTS simplemente comprueba si hay filas en el resultado.

Conceptualmente, un predicado EXISTS es equivalente a la recuperación de los resultados, el recuento de las filas devueltas y la comparación del recuento con cero. Compare las consultas siguientes, que devolverán detalles sobre los clientes que han realizado pedidos:

La primera consulta de ejemplo usa COUNT en una subconsulta:

SELECT CustomerID, CompanyName, EmailAddress 
FROM Sales.Customer AS c 
WHERE
(SELECT COUNT(*) 
  FROM Sales.SalesOrderHeader AS o
  WHERE o.CustomerID = c.CustomerID) > 0;

La segunda consulta, que devuelve los mismos resultados, usa EXISTS:

SELECT CustomerID, CompanyName, EmailAddress 
FROM Sales.Customer AS c 
WHERE EXISTS
(SELECT * 
  FROM Sales.SalesOrderHeader AS o
  WHERE o.CustomerID = c.CustomerID);

En el primer ejemplo, la subconsulta debe contar todas las repeticiones de cada custid que se encuentre en la tabla Sales.SalesOrderHeader y comparar los resultados del recuento con cero, simplemente para indicar que el cliente ha realizado pedidos.

En la segunda consulta, EXISTS devolverá TRUE para un custid en cuanto se haya encontrado un pedido pertinente en la tabla Sales.SalesOrderHeader. No es necesario realizar una contabilidad de cuentas completa de cada repetición. Tenga en cuenta también que, con el formulario EXISTS, la subconsulta no está restringida a la devolución de una sola columna. Aquí, aparece SELECT *. Las columnas devueltas son irrelevantes porque solo estamos comprobando si se devuelve alguna fila, no qué valores hay en esas filas.

Desde la perspectiva del procesamiento lógico, los dos formularios de consulta son equivalentes. Desde una perspectiva de rendimiento, el motor de base de datos puede tratar las consultas de forma diferente a medida que las optimiza para su ejecución. Considere la posibilidad de probar cada una de ellos para su propio uso.

Nota:

Si va a convertir una subconsulta mediante COUNT(*) en una con EXISTS, asegúrese de que la subconsulta usa SELECT * y no SELECT COUNT(*). SELECT COUNT(*) siempre devuelve una fila, de modo que EXISTS siempre devolverá TRUE.

Otra aplicación útil de EXISTS es la negación de la subconsulta con NOT, como en el ejemplo siguiente, que devolverá cualquier cliente que nunca haya realizado un pedido:

SELECT CustomerID, CompanyName, EmailAddress 
FROM SalesLT.Customer AS c 
WHERE NOT EXISTS
  (SELECT * 
   FROM SalesLT.SalesOrderHeader AS o
   WHERE o.CustomerID = c.CustomerID);

SQL Server tendrá que devolver datos sobre los pedidos relacionados para los clientes que hayan realizado pedidos. Si se encuentra un custid en la tabla Sales.SalesOrderHeader, NOT EXISTS se evalúa como FALSE y la evaluación se completa rápidamente.

Para escribir consultas que usan EXISTS con subconsultas, tenga en cuenta las siguientes directrices:

  • La palabra clave EXISTS sigue directamente a WHERE. Ningún nombre de columna (u otra expresión) la precede, a menos que también se utilice NOT.
  • En la subconsulta, use SELECT *. La subconsulta no devuelve ninguna fila, por lo que no es necesario especificar ninguna columna.