Páginas

martes, 7 de enero de 2014

4.10. Subconsultas correlacionadas

Una subconsulta correlacionada es una consulta anidada que contiene re-
ferencias a columnas de las tablas que se encuentran en el FROM de la consulta
principal. Son lo que se denomina referencias externas.
Como ya se ha visto, las subconsultas dotan al lenguaje SQL de una gran
potencia. Estas pueden utilizarse para hacer restricciones, tanto en la cláusula
WHERE como en la cláusula HAVING, y también en la cláusula FROM. Hasta ahora,
dichas subconsultas podían tratarse de modo independiente y, para comprender
mejor el funcionamiento de la sentencia, se podía suponer que la subconsulta
se ejecuta en primer lugar, y se sustituye ésta en la sentencia SELECT principal
por su valor, como se muestra en el siguiente ejemplo:
-- facturas con descuento máximo
SELECT *
FROM
facturas
WHERE
dto = ( SELECT MAX(dto) FROM facturas );
en primer lugar se obtiene el descuento máximo de las facturas, se sustituye la
subconsulta por este valor y, por último, se ejecuta la consulta principal.
87
88

4.10.1. Referencias externas

En ocasiones sucede que la subconsulta se debe recalcular para cada fila
de la consulta principal, estando la subconsulta parametrizada mediante va-
lores de columnas de la consulta principal. A este tipo de subconsultas se les
llama subconsultas correlacionadas y a los parámetros de la subconsulta que
pertenecen a la consulta principal se les llama referencias externas.
La siguiente sentencia obtiene los datos de las facturas que tienen descuento
en todas sus líneas:
SELECT *
FROM
facturas AS f
WHERE
0 < ( SELECT MIN(COALESCE(l.dto,0))
FROM
lineas_fac AS l
WHERE
l.codfac = f.codfac );
La referencia externa es f.codfac, ya que es una columna de la consulta prin-
cipal. En este caso, se puede imaginar que la consulta se ejecuta del siguiente
modo. Se recorre, fila a fila, la tabla de las facturas. Para cada fila se ejecuta
la subconsulta, sustituyendo f.codfac por el valor que tiene en la fila actual
de la consulta principal. Es decir, para cada factura se obtiene el descuento
mínimo en sus líneas. Si este descuento mínimo es mayor que cero, significa
que la factura tiene descuento en todas sus líneas, por lo que se muestra en
el resultado. Si no es así, la factura no se muestra. En cualquiera de los dos
casos, se continua procesando la siguiente factura: se obtienen sus líneas y el
descuento mínimo en ellas, etc.

4.10.2. Operadores EXISTS, NOT EXISTS

En un apartado anterior se han presentado los operadores que se pueden
utilizar con las subconsultas para hacer restricciones en las cláusulas WHERE
y HAVING. En aquel momento no se citó, intencionadamente, un operador, ya
que éste se utiliza siempre con referencias externas: el operador EXISTS.
EXISTS ( subconsulta )
La subconsulta se evalúa para determinar si devuelve o no alguna fila. Si
devuelve al menos una fila, se evalúa a verdadero. Si no devuelve ninguna
fila, se evalúa a falso. La subconsulta puede tener referencias externas,
que actuarán como constantes durante la evaluación de la subconsulta.
En la ejecución de la subconsulta, en cuanto se devuelve la primera fila,
se devuelve verdadero, sin terminar de obtener el resto de las filas.
Puesto que el resultado de la subconsulta carece de interés (sólo importa
si se devuelve o no alguna fila), se suelen escribir las consultas indicando
una constante en la cláusula SELECT en lugar de * o cualquier columna:

-- facturas que en alguna línea no tiene dto
SELECT *
FROM
facturas AS f
WHERE
EXISTS ( SELECT 1
FROM
lineas_fac AS l
WHERE
l.codfac = f.codfac
AND
COALESCE(dto,0)=0);
NOT EXISTS ( subconsulta )
La subconsulta se evalúa para determinar si devuelve o no alguna fila. Si
devuelve al menos una fila, se evalúa a falso. Si no devuelve ninguna fila,
se evalúa a verdadero. La subconsulta puede tener referencias externas,
que actuarán como constantes durante la evaluación de la subconsulta.
En la ejecución de la subconsulta, en cuanto se devuelve la primera fila,
se devuelve falso, sin terminar de obtener el resto de las filas.
Puesto que el resultado de la subconsulta carece de interés (sólo importa
si se devuelve o no alguna fila), se suelen escribir las consultas indicando
una constante en la cláusula SELECT en lugar de * o cualquier columna:
-- facturas que no tienen líneas sin descuento
SELECT *
FROM
facturas AS f
WHERE
NOT EXISTS ( SELECT 1
FROM
lineas_fac AS l
WHERE
l.codfac = f.codfac
AND
COALESCE(dto,0)=0);

4.10.3. Sentencias equivalentes

Algunos SGBD no son eficientes procesando consultas que tienen subcon-
sultas anidadas con referencias externas, por lo que es muy conveniente saber
encontrar sentencias equivalentes que no las utilicen, si es posible.
Por ejemplo, la siguiente sentencia también obtiene los datos de las facturas
que tienen descuento en todas sus líneas. Utiliza una subconsulta en la cláusula
FROM y no posee referencias externas.
SELECT *
FROM
facturas JOIN
( SELECT codfac
FROM
lineas_fac
GROUP BY codfac
HAVING MIN(COALESCE(dto,0))>0 ) AS lf
USING (codfac);

Una sentencia equivalente, que tampoco utiliza referencias externas, es la si-
guiente:
SELECT *
FROM
facturas
WHERE
codfac IN ( SELECT codfac
FROM
lineas_fac
GROUP BY codfac
HAVING MIN(COALESCE(dto,0))>0 );
4.10.4. Ejemplos
Ejemplo 4.16 ¿Cuántos clientes hay que en todas sus facturas han pagado
18% de IVA?




Ejemplo 4.18 Para proponer ofertas especiales a los buenos clientes, se ne-
cesita un listado con los datos de aquellos que en los últimos quince meses (los
últimos 450 días) han hecho siempre facturas por un importe superior a 400
e.
Se puede pensar en obtener el resultado recorriendo, uno a uno, los clientes.
Para cada cliente, comprobar, mediante una subconsulta, la restricción: que
todas sus facturas de los últimos 450 días tengan un importe superior a 400 e.
Ya que la subconsulta se ha de ejecutar para cada cliente, llevará una referencia
externa.
La restricción que se ha de cumplir sobre todas las facturas de ese periodo
se puede comprobar con ALL o con NOT EXISTS: o bien todas las facturas del

cliente (en el periodo) tienen un importe superior a 400 e, o bien no existen
facturas de ese cliente (en el periodo) con un importe igual o inferior a 400 e.
Se debe tener en cuenta que con los dos operadores (ALL, NOT EXISTS) se
obtendrán también en el resultado los clientes que no tienen ninguna factu-
ra, por lo que será preciso asegurarse de que los clientes seleccionados hayan
comprado en alguna ocasión en dicho periodo.
A continuación se muestran las dos versiones de la consulta que utilizan las
referencias externas tal y como se ha explicado.

En la siguiente versión se evitan las referencias externas utilizando opera-
dores de conjuntos. Obsérvese la subconsulta: del conjunto de los clientes que
alguna vez han comprado en ese periodo con facturas de más de 400 e, se
debe eliminar a aquellos que además han comprado alguna de 400 e o menos.
Puesto que se utiliza el operador IN, no es necesaria la restricción adicional
que comprueba que los clientes seleccionados hayan comprado alguna vez en
el periodo: si están en la lista es porque lo han hecho.

VOLVER AL INICIO DEL BLOG! :3

No hay comentarios:

Publicar un comentario