Subconsultas
Una subconsulta es una instrucción SELECT
anidada dentro de una instrucción SELECT,
SELECT...INTO,
INSERT...INTO,
DELETE, o
UPDATE o dentro de otra subconsulta.
Puede utilizar tres formas de sintaxis para crear una subconsulta:
comparación [ANY | ALL | SOME] (instrucción sql)
expresión [NOT] IN (instrucción sql)
[NOT] EXISTS (instrucción sql)
En donde:
- comparación
- Es una expresión y un operador de comparación que compara la expresión con
el resultado de la subconsulta.
- expresión
- Es una expresión por la que se busca el conjunto resultante de la
subconsulta.
- instrucción sql
- Es una instrucción SELECT, que sigue el mismo
formato y reglas que cualquier otra instrucción
SELECT. Debe ir entre paréntesis.
Se puede utilizar una subconsulta en lugar de una expresión en la lista de
campos de una instrucción SELECT o en una
cláusula WHERE o
HAVING. En una subconsulta, se utiliza una
instrucción SELECT para proporcionar un conjunto
de uno o más valores especificados para evaluar en la expresión de la cláusula
WHERE o HAVING.
Se puede utilizar el predicado ANY o
SOME, los cuales son sinónimos, para recuperar
registros de la consulta principal, que satisfagan la comparación con cualquier
otro registro recuperado en la subconsulta. El ejemplo siguiente devuelve todos
los productos cuyo precio unitario es mayor que el de cualquier producto vendido
con un descuento igual o mayor al 25 por ciento:
SELECT * FROM Productos WHERE PrecioUnidad > ANY
(SELECT PrecioUnidad FROM DetallePedido WHERE Descuento >= 0 .25);
El predicado ALL se utiliza para recuperar
únicamente aquellos registros de la consulta principal que satisfacen la
comparación con todos los registros recuperados en la subconsulta. Si se cambia
ANY por ALL en el
ejemplo anterior, la consulta devolverá únicamente aquellos productos cuyo
precio unitario sea mayor que el de todos los productos vendidos con un
descuento igual o mayor al 25 por ciento. Esto es mucho más restrictivo.
El predicado IN se emplea para recuperar
únicamente aquellos registros de la consulta principal para los que algunos
registros de la subconsulta contienen un valor igual. El ejemplo siguiente
devuelve todos los productos vendidos con un descuento igual o mayor al 25 por
ciento:
SELECT * FROM Productos WHERE IDProducto IN
(SELECT IDProducto FROM DetallePedido WHERE Descuento >= 0.25);
Inversamente se puede utilizar NOT IN para
recuperar únicamente aquellos registros de la consulta principal para los que
no hay ningún registro de la subconsulta que contenga un valor igual.
El predicado EXISTS (con la palabra
reservada NOT opcional) se utiliza en
comparaciones de verdad/falso para determinar si la subconsulta devuelve algún
registro. Supongamos que deseamos recuperar todos aquellos clientes que hayan
realizado al menos un pedido:
SELECT Clientes.Compañía, Clientes.Teléfono FROM Clientes WHERE EXISTS
(SELECT FROM Pedidos WHERE Pedidos.IdPedido = Clientes.IdCliente)
Esta consulta es equivalente a esta otra:
SELECT Clientes.Compañía, Clientes.Teléfono FROM Clientes WHERE IdClientes IN
(SELECT Pedidos.IdCliente FROM Pedidos)
Se puede utilizar también alias del nombre de la tabla en una subconsulta
para referirse a tablas listadas en la cláusula
FROM fuera de la subconsulta. El ejemplo
siguiente devuelve los nombres de los empleados cuyo salario es igual o mayor
que el salario medio de todos los empleados con el mismo título. A la tabla
Empleados se le ha dado el alias T1:
SELECT Apellido, Nombre, Titulo, Salario FROM Empleados AS T1
WHERE Salario >= (SELECT Avg(Salario) FROM Empleados
WHERE T1.Titulo = Empleados.Titulo) ORDER BY Titulo;
En el ejemplo anterior , la palabra reservada
AS es opcional. Otros ejemplos:
SELECT Apellidos, Nombre, Cargo, Salario FROM Empleados
WHERE Cargo LIKE "Agente Ven*" AND Salario > ALL (SELECT Salario FROM
Empleados WHERE (Cargo LIKE "*Jefe*") OR (Cargo LIKE "*Director*"));
- Obtiene una lista con el nombre, cargo y salario de todos los agentes de
ventas cuyo salario es mayor que el de todos los jefes y directores.
SELECT DISTINCTROW NombreProducto, Precio_Unidad FROM Productos
WHERE (Precio_Unidad = (SELECT Precio_Unidad FROM Productos WHERE
Nombre_Producto = "Almíbar anisado");
- Obtiene una lista con el nombre y el precio unitario de todos los
productos con el mismo precio que el almíbar anisado.
SELECT DISTINCTROW Nombre_Contacto, Nombre_Compañia, Cargo_Contacto,
Telefono FROM Clientes WHERE (ID_Cliente IN (SELECT DISTINCTROW
ID_Cliente FROM Pedidos WHERE Fecha_Pedido >= #04/1/93# <#07/1/93#);
- Obtiene una lista de las compañías y los contactos de todos los clientes
que han realizado un pedido en el segundo trimestre de 1993.
SELECT Nombre, Apellidos FROM Empleados AS E WHERE EXISTS
(SELECT * FROM Pedidos AS O WHERE O.ID_Empleado = E.ID_Empleado);
- Selecciona el nombre de todos los empleados que han reservado al menos un
pedido.
SELECT DISTINCTROW Pedidos.Id_Producto, Pedidos.Cantidad,
(SELECT DISTINCTROW Productos.Nombre FROM Productos WHERE
Productos.Id_Producto = Pedidos.Id_Producto) AS ElProducto FROM
Pedidos WHERE Pedidos.Cantidad > 150 ORDER BY Pedidos.Id_Producto;
- Recupera el Código del Producto y la Cantidad pedida de la tabla pedidos,
extrayendo el nombre del producto de la tabla de productos.
SELECT NumVuelo, Plazas FROM Vuelos
WHERE Origen = 'Madrid' AND Exists
(SELECT T1.NumVuelo FROM Vuelos AS T1
WHERE T1.PlazasLibres > 0 AND T1.NumVuelo=Vuelos.NumVuelo)
- Recupera números de vuelo y capacidades de aquellos vuelos con destino Madrid y plazas libres.
Supongamos ahora que tenemos una tabla con los identificadores de todos nuestros productos y el stock de cada uno de ellos. En otra tabla se encuentran todos los pedidos que tenemos pendientes de servir. Se trata de averiguar que productos no se podemos servir por falta de stock.
SELECT PedidosPendientes.Nombre FROM PedidosPendientes
GROUP BY PedidosPendientes.Nombre
HAVING SUM(PedidosPendientes.Cantidad <
(SELECT Productos.Stock FROM Productos
WHERE Productos.IdProducto = PedidosPendientes.IdProducto));
Supongamos que en nuestra tabla de empleados deseamos buscar todas las mujeres cuya edad sea mayor a la de cualquier hombre:
SELECT Empleados.Nombre FROM Empleados
WHERE Sexo = 'M' AND Edad > ANY
(SELECT Empleados.Edad FROM Empleados WHERE Sexo ='H')
ó lo que sería lo mismo:
SELECT Empleados.Nombre FROM Empleados WHERE Sexo = 'M' AND Edad >
(SELECT Max( Empleados.Edad )FROM Empleados WHERE Sexo ='H')
La siguiente tabla muestra algún ejemplo del operador ANY y ALL
| Valor 1 |
Operador |
Valor 2 |
Resultado |
| 3 |
> ANY |
(2,5,7) |
Cierto |
| 3 |
= ANY |
(2,5,7) |
Falso |
| 3 |
= ANY |
(2,3,5,7) |
Cierto |
| 3 |
> ANY |
(2,5,7) |
Falso |
| 3 |
< ANY |
(5,6,7) |
Falso |
El operacion =ANY es equivalente al operador IN, ambos devuelven el mismo resultado.