Apuntes de BD para DAW, DAM y ASIR
José Juan Sánchez Hernández
Curso 2023/2024
Una subconsulta es una consulta anidada dentro de otra consulta.
Debe tener en cuenta que no existe una única solución para resolver una consulta en SQL. En esta unidad vamos a estudiar cómo podemos resolver haciendo uso de subconsultas, algunas de las consultas que hemos resuelto en las unidades anteriores.
El estándar SQL define tres tipos de subconsultas:
WHERE
Por ejemplo, suponga que queremos conocer el nombre del producto que tiene el mayor precio. En este caso podríamos realizar una primera consulta para buscar cuál es el valor del precio máximo y otra segunda consulta para buscar el nombre del producto cuyo precio coincide con el valor del precio máximo. La consulta sería la siguiente:
En este caso sólo hay un nivel de anidamiento entre consultas pero pueden existir varios niveles de anidamiento.
HAVING
Ejemplo: Devuelve un listado con todos los nombres
de los fabricantes que tienen el mismo número de productos que el
fabricante Asus
.
SELECT fabricante.nombre, COUNT(producto.codigo)
FROM fabricante INNER JOIN producto
ON fabricante.codigo = producto.codigo_fabricante
GROUP BY fabricante.codigo
HAVING COUNT(producto.codigo) >= (
SELECT COUNT(producto.codigo)
FROM fabricante INNER JOIN producto
ON fabricante.codigo = producto.codigo_fabricante
WHERE fabricante.nombre = 'Asus');
FROM
Ejemplo: Devuelve una listado de todos los productos que tienen un precio mayor o igual al precio medio de todos los productos de su mismo fabricante.
SELECT *
FROM producto INNER JOIN (
SELECT codigo_fabricante, AVG(precio) AS media
FROM producto
GROUP BY codigo_fabricante) AS t
ON producto.codigo_fabricante = t.codigo_fabricante
WHERE producto.precio >= t.media;
SELECT
Las subconsultas que pueden aparecer en la cláusula
SELECT
tienen que ser subconsultas de tipo
escalar, que devuelven una única fila y columna.
Imagen: Imagen extraída del libro «SQL Queries for Mere Mortals» de John L. Viescas.
Los operadores que podemos usar en las subconsultas son los siguientes:
ALL
y ANY
.IN
y NOT IN
.EXISTS
y NOT EXISTS
.Los operadores básicos de comparación (>,>=, <, <=, !=, <>, =) se pueden usar cuando queremos comparar una expresión con el valor que devuelve una subconsulta.
Los operadores básicos de comparación los vamos a utilizar para realizar comparaciones con subconsultas que devuelven un único valor, es decir, una columna y una fila.
Ejemplo: Devuelve todos los productos de la base de
datos que tienen un precio mayor o igual al producto más caro del
fabricante Asus
.
SELECT *
FROM producto
WHERE precio >= (SELECT MAX(precio)
FROM fabricante INNER JOIN producto
ON fabricante.codigo = producto.codigo_fabricante
WHERE fabricante.nombre = 'Asus');
La consulta anterior también se puede escribir con subconsultas sin
hacer uso de INNER JOIN
.
SELECT *
FROM producto
WHERE precio = (
SELECT MAX(precio)
FROM producto
WHERE codigo_fabricante = (
SELECT codigo
FROM fabricante
WHERE nombre = 'Asus'));
ALL
y ANY
ALL
y ANY
se utilizan con los operadores de
comparación (>,>=, <, <=, !=, <>, =) y nos permiten
comparar una expresión con el conjunto de valores que devuelve una
subconsulta.
ALL
y ANY
los vamos a utilizar para
realizar comparaciones con subconsultas que pueden devolver
varios valores, es decir, una columna y varias filas.
Ejemplo: Podemos escribir la consulta que devuelve
todos los productos de la base de datos que tienen un precio mayor o
igual al producto más caro del fabricante Asus
, haciendo
uso de ALL
. Por lo tanto estas dos consultas darían el
mismo resultado.
SELECT *
FROM fabricante INNER JOIN producto
ON fabricante.codigo = producto.codigo_fabricante
WHERE precio >= (SELECT MAX(precio)
FROM fabricante INNER JOIN producto
ON fabricante.codigo = producto.codigo_fabricante
WHERE fabricante.nombre = 'Asus');
SELECT *
FROM fabricante INNER JOIN producto
ON fabricante.codigo = producto.codigo_fabricante
WHERE precio >= ALL (SELECT precio
FROM fabricante INNER JOIN producto
ON fabricante.codigo = producto.codigo_fabricante
WHERE fabricante.nombre = 'Asus');
La palabra reservada SOME
es un alias de
ANY
. Por lo tanto, las siguientes consultas devolverían el
mismo resultado:
SELECT s1 FROM t1 WHERE s1 <> ANY (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 <> SOME (SELECT s1 FROM t2);
IN
y NOT IN
IN
y NOT IN
nos permiten comprobar si un
valor está o no incluido en un conjunto de valores, que puede ser el
conjunto de valores que devuelve una subconsulta.
IN
y NOT IN
los vamos a utilizar para
realizar comparaciones con subconsultas que pueden devolver
varios valores, es decir, una columna y varias filas.
Ejemplo: Devuelve un listado de los clientes que no han realizado ningún pedido.
Cuando estamos trabajando con subconsultas, IN
y
= ANY
realizan la misma función. Por lo tanto, las
siguientes consultas devolverían el mismo resultado:
SELECT s1 FROM t1 WHERE s1 = ANY (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 IN (SELECT s1 FROM t2);
Ocurre lo mismo con NOT IN
y <> ALL
.
Por lo tanto, las siguientes consultas devolverían el mismo
resultado:
SELECT s1 FROM t1 WHERE s1 <> ALL (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 NOT IN (SELECT s1 FROM t2);
Importante:
Tenga en cuenta que cuando hay un valor NULL
en el
resultado de la consulta interna, la consulta externa no devuelve ningún
valor.
Ejemplo: Devuelve un listado con el nombre de los departamentos que no tienen empleados asociados.
La consulta interna
SELECT codigo_departamento FROM empleado
, devuelve algunas
filas con valores NULL
y por lo tanto la consulta externa
no devuelve ningún valor.
La forma de solucionarlo sería quitando los valores NULL
de la consulta interna:
SELECT nombre
FROM departamento
WHERE codigo NOT IN (
SELECT codigo_departamento
FROM empleado
WHERE codigo_departamento IS NOT NULL);
EXISTS
y NOT EXISTS
Ejemplo: Devuelve un listado de los clientes que no han realizado ningún pedido.
ERROR 1242 (ER_SUBSELECT_NO_1_ROW)
SQLSTATE = 21000
Message = "Subquery returns more than 1 row"
La consulta anterior sólo se podrá ejecutar cuando la consulta
interna SELECT column1 FROM t2
devuelva una única fila.
Esta
página forma parte del curso
Bases de Datos de
José Juan Sánchez Hernández y
su contenido se distribuye bajo una
licencia
Creative Commons Reconocimiento-NoComercial-CompartirIgual 4.0
Internacional.