Subconsultas (Subqueries)
Las subconsultas son consultas auxiliares cuyo resultado servirá para hacer una consulta de mayor jerarquía:
Las subconsultas son de sólo lectura (comando SELECT
),
por ello no pueden modificar los datos guardados por sí mismas.
Orden
El orden de la subconsulta es la cantidad de subconsultas anidadas. Si hay una es orden 1, si hay dos es orden dos, etc. Cada gestor de bases de datos impone su propio límite al orden de las subconsultas admisibles.
EL estándar SQL impone un máximo de 16 subconsultas, pero hay gestores que soportan muchas más.
Tipos
Las subqueries pueden clasificarse por tipos según resultado buscado:
- subconsultas de valor unico
- subconsultas de fila
- subconsultas de tabla
También pueden clasificarse según su correlacion con la tabla principal:
- correlacionales
- no correlacionales
Sin embargo, en el lenguaje coloquial rara vez se especifica qué tipo de subconsulta se usó en cada caso.
Uso
Se consultan los productos y cantidades de la tabla de pedidos,
llamada OrderDetails
...
... pero se devuelven los IDs de los productos, no su nombre:
ProductID | Cantidad |
---|---|
11 | 12 |
42 | 10 |
72 | 5 |
... | ... |
Éste se encuentra en otra tabla llamada Products
.
ProductID | Nombre |
---|---|
1 | Chais |
2 | Chang |
3 | Aniseed Syrup |
4 | Chef Anton's Cajun Seasoning |
... | ... |
La última instrucción se agrega adentro de la primera como subconsulta:
SELECT
ProductID,
Quantity,
-- Subconsulta a la tabla 'Products'
(SELECT ProductName FROM Products) AS Nombre -- MAL: nombre producto erróneo
FROM OrderDetails;
ProductID
:
-- Consulta a la tabla 'OrderDetails'
SELECT ProductID as pID,
Quantity as Cantidad,
-- Subconsulta a la tabla 'Products'- con condición de acoplamiento
(SELECT ProductName FROM Products
WHERE OrderDetails.ProductID = ProductID) AS Producto
FROM OrderDetails;
pID | Cantidad | Producto |
---|---|---|
11 | 12 | Queso Cabrales |
42 | 10 | Singaporean Hokkien Fried Mee |
72 | 5 | Mozzarella di Giovanni |
... | ... | ... |
Alias de tabla
Las tablas obtenidas pueden tener un alias,
un nombre alternativo para poder consultarlas.
Este alias se crea con ayuda del operador AS
o con el uso de corchetes:
En el ejemplo se repite la subconsulta previa implementando un alias para la tabla de salida:
-- Consulta a la tabla 'OrderDetails'
SELECT ProductID,
Quantity,
-- Subconsulta a la tabla 'Products'
(SELECT ProductName FROM Products AS Prod
WHERE Od.ProductID = ProductID) AS Nombre
FROM OrderDetails AS Od -- 'Od': alias de tabla
Múltiples subconsultas
Una misma consulta puede incluir varias subconsultas internas.
En el ejemplo se consultan el nombre de cada producto y precio por separado:
-- Consulta a tabla
SELECT ProductID,
Quantity AS Cantidad,
-- Subconsulta Nº1 a la tabla 'Products': nombre producto
(SELECT ProductName FROM Products
WHERE Od.ProductID = ProductID) AS Producto,
-- Subconsulta Nº2 a la tabla 'Products': precio
(SELECT Price FROM Products
WHERE Od.ProductID = ProductID) AS Precio
FROM OrderDetails AS Od
Este es el resultado:
ProductID | Cantidad | Producto | Precio |
---|---|---|---|
11 | 12 | Queso Cabrales | 21 |
42 | 10 | Singaporean Hokkien Fried Mee | 14 |
72 | 5 | Mozzarella di Giovanni | 34.8 |
14 | 9 | Tofu | 23.25 |
... | ... | ... | ... |
Subconsultas de orden superior
Las subconsultas de orden superior se consiguen anidando subconsultas.
En este ejemplo, se consulta el precio de los productos para poder calcular el dinero recaudado por producto (cantidad * precio
)
SELECT ProductID,
SUM(Quantity) as Cantidad,
-- subconsulta de primer orden: precio
(SELECT Price FROM Products
WHERE ProductID = OD.ProductID) AS Precio,
-- subconsulta de segundo orden: total recaudado por ventas
SUM(Quantity)*(
-- sub-subconsulta: precios de cada producto
SELECT Price FROM Products
WHERE ProductID = OD.ProductID
) AS Total_Recaudado
FROM OrderDetails AS OD
GROUP BY ProductID
ORDER BY Total_Recaudado DESC
Resultado:
ProductID | Cantidad | Precio | Total_Recaudado |
---|---|---|---|
38 | 239 | 263.5 | 62976.5 |
29 | 168 | 123.79 | 20796.72 |
59 | 346 | 55 | 19030 |
... | ... | ... | ... |
Si al ejemplo previo se le agrega una nueva subconsulta se puede adjuntar también el nombre de cada producto:
SELECT ProductID,
-- Subconsulta Nº1 a la tabla 'Products': nombre producto
(SELECT ProductName FROM Products WHERE ProductID = OD.ProductID) as Producto,
-- Subconsulta Nº2 a la tabla 'Products': precio
(SELECT Price FROM Products WHERE ProductID = OD.ProductID) as Precio,
SUM(Quantity) AS Cantidad,
-- Subconsulta Nº3 a la tabla 'Products' (segundo orden): total recaudado por ventas
(SUM(Quantity) * (
-- sub-subconsulta: precios de cada producto
SELECT Price FROM Products WHERE ProductID = OD.ProductID)
) AS Total_Recaudado
FROM [OrderDetails] OD
GROUP BY ProductID
ORDER BY total_recaudado DESC;
Resultado:
ProductID | Producto | Precio | Cantidad | Total_Recaudado |
---|---|---|---|---|
38 | Côte de Blaye | 263.5 | 239 | 62976.5 |
29 | Thüringer Rostbratwurst | 123.79 | 168 | 20796.72 |
59 | Raclette Courdavault | 55 | 346 | 19030 |
... | ... | ... | ... | ... |
Ejemplos adicionales
Ordenado de empleados por Nº de ventas
Esta consulta cuenta el total de objetos vendidos por cada empleado.
SELECT FirstName,LastName,
(
SELECT SUM(od.Quantity) FROM [orders] o, [OrderDetails] od
WHERE o.EmployeeID = e.EmployeeID AND od.OrderID=o.orderID
) AS unidades_totales
FROM [Employees] e
Hay 10 empleados en la tienda:
FirstName | LastName | unidades_totales |
---|---|---|
Nancy | Davolio | 1924 |
Andrew | Fuller | 1315 |
Janet | Leverling | 1725 |
Margaret | Peacock | 3232 |
Steven | Buchanan | 778 |
Michael | Suyama | 1094 |
Robert | King | 733 |
Laura | Callahan | 1293 |
Anne | Dodsworth | 649 |
Adam | West | Null |
(el último no tiene ninguna venta registrada a su nombre)
Empleados que vendieron más de la media
SELECT FirstName, LastName, (
-- subquerie: ventas por empleado
SELECT SUM(od.Quantity) FROM [orders] o, [OrderDetails] od
WHERE o.EmployeeID = e.EmployeeID AND od.OrderID=o.orderID
) AS unidades_totales
FROM [Employees] e
-- sección filtrado --> media sobre tabla virtual
WHERE unidades_totales < (
-- cálculo de la media de ventas por empleado
SELECT AVG(unidades_totales) FROM (
SELECT (
-- subquerie: ventas por empleado
SELECT SUM(od.Quantity) FROM [orders] o, [OrderDetails] od
WHERE o.EmployeeID=e2.EmployeeID AND od.orderID=o.orderID
) AS unidades_totales
FROM [Employees] e2
GROUP BY e2.EmployeeID
)
)
Hay 6 empleados cuya cantidad de ventas supera la media:
FirstName | LastName | unidades_totales |
---|---|---|
Andrew | Fuller | 1315 |
Steven | Buchanan | 778 |
Michael | Suyama | 1094 |
Robert | King | 733 |
Laura | Callahan | 1293 |
Anne | Dodsworth | 649 |
Nótese que la subconsulta del ejemplo previo debió repetirse dos veces para obtener el resultado final. Una alternativa superadora a las subqueries son las uniones o joins, que se explican más adelante.