Está en la página 1de 27

Ejercicios resueltos de SQL

En mi navegación por la red encontré estos "Ejercicios resueltos de SQL"


de Borja Sotomayor en los que a lo largo de 9 capítulos, cada uno de ellos con
un diseño básico de una base de datos, se nos plantean diferentes consultas,
tanto de selección como de acción y Borja nos ofrece su solución en lenguaje
SQL.

Constituyen una muy buena práctica en creación de consultas, que no precisan


del dominio de SQL para ser resueltas, pues podemos solucionarlas con el
diseñador gráfico de consultas que incorpora cualquier base de datos de
escritorio (Access, OpenOffice.org Base, etc).

También pueden constituir una buena base de ideas para desarrollar diferentes
ejercicios en un sentido más amplio, como he hecho, por ejemplo,
en Investigadores, Facultades y Equipos .

Los capítulos, cada uno con la estructura de tablas y relaciones de una base de
datos, son:

1. La Tienda de Informática
2. Empleados
3. Los Almacenes
4. Películas y Salas
5. Los Directores
6. Piezas y Proveedores
7. Los Científicos
8. Los Grandes Almacenes
9. Los Investigadores

Espero que los disfrutéis. En cuanto me sea posible, subiré bases de datos en
formato Access o Base, con suficientes datos de ejemplo para poder realizar
los ejercicios y comprobar su resultado sin necesidad de mecanografiar.

Nota: En el diseño de las bases de datos, veréis que los tipos de datos
utilizados, difieren de los de Access. La equivalencia es bien fácil:

 int -> Entero


 char -> Texto
 varchar -> Texto
 nvarchar -> Texto

Disfrutadlos.
Resolución de ejercicios

/*

EJERCICIOS

PROPUESTOS

**********************************************************************
*

EJERCICIOS

**********************************************************************
**

1. Actualizar el precio unitario de los productos de la categoría


CARNICOS, subiéndolos en un 10% (1)

*/

select * from productos;

update productos

set preciounit= (preciounit*0.1) + preciounit;

/*

2. Actualizar el teléfono celular del proveedor cuyo contacto es


MANUEL ANDRADE, con el valor 099010291 (2)

*/

select * from proveedores;

update proveedores

set celuprov= '0990101291'

where contacto= 'MANUEL ANDRADE';

/*

3. Borrar el producto YOGURT DE


SABORES (3)

*/

select * from productos;


delete from productos

where descripcion ='YOGURT DE SABORES';

/*

4. Realizar las siguientes consultas:

4.1 Mostrar todas las órdenes: el id de la orden, el apellido y nombre


del empleado que la atendió (4)

el nombre de la compañía cliente y la fecha de orden

*/

SELECT * FROM ORDENES;

select * from empleados;

select * from clientes;

select ord.ordenid, emp.empleadoid, emp.nombre, emp.apellido,ord.fecha


orden, cli.clienteid, cli.nombrecia

from ordenes ord, empleados emp, clientes cli

where ord.empleadoid=emp.empleadoid andord.clienteid=cli.clienteid ;

/*

4.2 Mostrar la suma total de cada tipo de producto pedidos en todas


las órdenes. (5)

*/

select * from detalle_ordenes;

select productoid, sum(cantidad) as "Suma total"

from detalle_ordenes

group by productoid;

/*

4.3 Mostrar el número de órdenes atendidas por cada empleado,


incluidos los que tienen 0 órdenes. (6)

*/

select * from empleados;

select * from ordenes;


select nombre as "Nombre" , apellido as "Apellido", count(ord.empleado
id) as "# de ordenes"

from empleados emp

left join ordenes ord

on emp.empleadoid= ord.empleadoid

group by nombre, apellido;

/*

4.4 Muestre los proveedores y la suma de dinero vendido en los


productos de ese proveedor. (7)

*/

select * from proveedores;

select * from productos;

select * from detalle_ordenes;

select prov.nombreprov as "Nombre",sum(d.cantidad *prod.preciounit ) a


s "Suma dinero"

from proveedores prov, productos prod, detalle_ordenes d

where d.productoid= prod.productoid and prov.proveedorid=prod.proveedo


rid

group by nombreprov;

/*

5. Realizar el siguiente procedimiento almacenado.

5.1 Escriba un procedimiento almacenado que reciba como parámetro un


código de proveedor y (8)

devuelve el número de órdenes en las que están incluidos productos de


ese proveedor.

*/

DROP LANGUAGE IF EXISTS plpgsql;

CREATE LANGUAGE plpgsql;

select * from productos;

select * from ordenes;


select * from detalle_ordenes;

create or replace function num_ordene_proveedor(idproo int)returns int


eger

as $$

declare

num int;

begin

select count(p.productoid) into num from productos pjoin d


etalle_ordenes dor

on (p.productoid=dor.productoid)join ordenes ordon(ord.ord


enid=dor.ordenid)

where proveedorid=idproo

group by proveedorid;

return num;

end;

$$ LANGUAGE plpgsql;

select num_ordene_proveedor(10)

/*

5.2 Escriba un procedimiento almacenado que reciba como parámetro un


nombre de una categoría y (9)

devuelve el código del producto de esa categoría que tiene más


unidades vendidas.

*/

select * from categorias;

select * from productos;

select * from detalle_ordenes;

create or replace function mas_vendido (nomCat character(50))returns i


nteger

as $$

declare

num int;

begin
select p.productoid,sum(cantidad)INTO NUM fromproductos p
join categorias c on (c.categoriaid=p.categoriaid)

join detalle_ordenes dor on(p.productoid=dor.productoid)

where c.nombrecat=nomCat

group by descripcion, p.productoid

order by sum DESC

limit 1;

return num;

end;

$$LANGUAGE 'plpgsql';

select mas_vendido('CARNICOS')
UF1888-E4: Ejercicio SQL
Objetivos:

Llevar a la práctica los conocimientos de SQL adquiridos.

Enunciado:

Los ejercicios a realizar van a recuperar datos de dos tablas:

 la tabla de empleados (TEMPLE1)


 la de departamentos (TDEPAR2)

Contenido de datos

Los nombres reales y características de las columnas se muestran en la siguiente


tabla:

TEMPLE1 (Tabla de Empleados)

nuempl nombre i apellido dept tlfn feching ctr nv s

10 CRISTINA I HERNANDEZ A00 2070 01.01.1965 66 18 M

20 MIGUEL L TAPIA B01 2140 10.10.1973 61 18 H

30 SALOME A KEMPES C01 2080 05.04.1975 60 20 M

50 JUAN B GARRIDO E01 1507 17.08.1949 58 16 H

60 ISIDRO F SUAREZ D11 2290 14.09.1973 55 16 H

70 EVA D PUENTE D21 2400 30.09.1980 56 16 M

90 ELENA W HERRANZ E11 2230 15.08.1970 55 16 M

100 TOMAS Q SOLER E21 2170 19.06.1980 54 14 H


nuempl nombre i apellido dept tlfn feching ctr nv s

110 VICENTE G LUENGO A00 1140 16.05.1958 58 19 H

120 SIMON - OTERO A00 2022 05.12.1963 58 14 H

130 DOLORES M QUINTANA C01 2140 28.07.1971 55 16 M

140 HELIODORA A NIETO C01 2210 15.12.1976 56 18 M

150 BRUNO - ALVAREZ D11 2250 12.02.1971 55 16 H

160 ELISA R PINTO D11 1307 11.10.1977 54 17 M

170 MATEO J YARZA D11 2050 15.09.1978 54 16 H

180 MARINA S SANDOVAL D11 1050 07.07.1973 53 17 M

190 JAIME H WALKER D11 2100 27.07.1974 53 16 H

200 DAVID - BONDIA D11 2340 03.03.1966 55 16 H

210 WENCESLAO T JURADO D11 2310 11.04.1979 52 17 H

220 JIMENA K LUQUE D11 2300 29.08.1968 55 18 M

230 JAIME J JIMENEZ D21 1080 21.11.1966 53 14 H

240 SALVADOR M MARTINEZ D21 2260 05.12.1979 55 17 H

250 DANIEL S SIERRA D21 2357 30.10.1969 52 15 H

260 SUSANA P JUNQUERA D21 2302 11.09.1975 52 16 M

270 MARIA L PEREZ D21 2217 30.09.1980 55 15 M


nuempl nombre i apellido dept tlfn feching ctr nv s

280 ENGRACIA R SANCHEZ E11 2007 24.03.1967 54 17 M

290 JUAN R PALACIOS E11 2192 30.05.1980 42 12 H

300 PEDRO I SIERRA E11 3522 19.06.1972 48 14 H

310 MATILDE F SERNA E11 2130 12.09.1964 43 12 M

320 RAMON V MORAN E21 7112 07.07.1965 52 16 H

330 WILLY - LERMA E21 1132 23.02.1976 55 14 H

340 JAVIER R GIL E21 1162 05.05.1947 54 16 H

999 ANGEL E MALDONADO E11 ---- 22.03.1988 69 20 H

TDEPAR2 (Tabla de Departamentos)

numdep nomdep

A00 CENTRO PROCESOS 10

B01 PLANIFICACION 20

C01 INFORMACION 30

D01 DESARROLLO 200

D11 FABRICACION 60

D21 ADMINISTRACION 70

E01 SERVICIOS 50
numdep nomdep

E11 OPERACIONES 90

E21 SOFTWARE 100

Resolución

Creación de las tablas


CREATE TABLE temple1 (
nuempl CHAR(6) NOT NULL,
nombre CHAR(12) NOT NULL,
inicial CHAR(1) NOT NULL,
apellido CHAR(15) NOT NULL,
dept CHAR(3) NOT NULL,
tlfn CHAR(4),
feching DATE NOT NULL,
codtra SMALLINT NOT NULL,
niveduc SMALLINT NOT NULL,
sexo CHAR(1) NOT NULL,
fechnac DATE NOT NULL,
salario DECIMAL(9,2) NOT NULL
);
CREATE TABLE tdepar2 (
numdep CHAR(3) NOT NULL,
nomdep CHAR(36) NOT NULL,
numdirec CHAR(6) NOT NULL
);

Inserción de datos
SET DATESTYLE TO 'European';
INSERT INTO temple1 ( nuempl, nombre, inicial, apellido, dept, tlfn, feching,
codtra, niveduc, sexo, fechnac, salario )
VALUES ( 10, 'CRISTINA', 'I', 'HERNANDEZ', 'A00', 2070,
'01.01.1965', 66, 18, 'M', '14.08.1933', 5275 ),
( 20, 'MIGUEL', 'L', 'TAPIA', 'B01', 2140, '10.10.1973', 61,
18, 'H', '02.02.1948', 4125 ),
( 30, 'SALOME', 'A', 'KEMPES', 'C01', 2080, '05.04.1975',
60, 20, 'M', '11.05.1941', 3825 ),
( 50, 'JUAN', 'B', 'GARRIDO', 'E01', 1507, '17.08.1949', 58,
16, 'H', '15.09.1925', 4017 ),
( 60, 'ISIDRO', 'F', 'SUAREZ', 'D11', 2290, '14.09.1973',
55, 16, 'H', '07.07.1945', 3225 ),
( 70, 'EVA', 'D', 'PUENTE', 'D21', 2400, '30.09.1980', 56,
16, 'M', '26.05.1953', 3617 ),
( 90, 'ELENA', 'W', 'HERRANZ', 'E11', 2230, '15.08.1970',
55, 16, 'M', '15.05.1941', 2945 ),
( 100, 'TOMAS', 'Q', 'SOLER', 'E21', 2170, '19.06.1980', 54,
14, 'H', '18.12.1956', 2615 ),
( 110, 'VICENTE', 'G', 'LUENGO', 'A00', 1140, '16.05.1958',
58, 19, 'H', '05.11.1929', 4650 ),
( 120, 'SIMON', '', 'OTERO', 'A00', 2022, '05.12.1963', 58,
14, 'H', '18.10.1942', 2925 ),
( 130, 'DOLORES', 'M', 'QUINTANA', 'C01', 2140,
'28.07.1971', 55, 16, 'M', '15.09.1925', 2380 ),
( 140, 'HELIODORA', 'A', 'NIETO', 'C01', 2210, '15.12.1976',
56, 18, 'M', '19.01.1946', 2842 ),
( 150, 'BRUNO', '', 'ALVAREZ', 'D11', 2250, '12.02.1971',
55, 16, 'H', '17.05.1947', 2528 ),
( 160, 'ELISA', 'R', 'PINTO', 'D11', 1307, '11.10.1977', 54,
17, 'M', '12.04.1955', 2225 ),
( 170, 'MATEO', 'J', 'YARZA', 'D11', 2050, '15.09.1978', 54,
16, 'H', '05.01.1951', 2468 ),
( 180, 'MARINA', 'S', 'SANDOVAL', 'D11', 1050, '07.07.1973',
53, 17, 'M', '21.02.1949', 2134 ),
( 190, 'JAIME', 'H', 'WALKER', 'D11', 2100, '27.07.1974',
53, 16, 'H', '25.06.1952', 2045 ),
( 200, 'DAVID', '', 'BONDIA', 'D11', 2340, '03.03.1966', 55,
16, 'H', '29.05.1941', 2774 ),
( 210, 'WENCESLAO', 'T', 'JURADO', 'D11', 2310,
'11.04.1979', 52, 17, 'H', '23.02.1953', 1827 ),
( 220, 'JIMENA', 'K', 'LUQUE', 'D11', 2300, '29.08.1968',
55, 18, 'M', '19.03.1948', 2984 ),
( 230, 'JAIME', 'J', 'JIMENEZ', 'D21', 1080, '21.11.1966',
53, 14, 'H', '30.05.1935', 2218 ),
( 240, 'SALVADOR', 'M', 'MARTINEZ', 'D21', 2260,
'05.12.1979', 55, 17, 'H', '31.03.1954', 2876 ),
( 250, 'DANIEL', 'S', 'SIERRA', 'D21', 2357, '30.10.1969',
52, 15, 'H', '12.11.1939', 1918 ),
( 260, 'SUSANA', 'P', 'JUNQUERA', 'D21', 2302, '11.09.1975',
52, 16, 'M', '05.10.1936', 1725 ),
( 270, 'MARIA', 'L', 'PEREZ', 'D21', 2217, '30.09.1980', 55,
15, 'M', '23.05.1953', 2738 ),
( 280, 'ENGRACIA', 'R', 'SANCHEZ', 'E11', 2007,
'24.03.1967', 54, 17, 'M', '15.05.1941', 2598 ),
( 290, 'JUAN', 'R', 'PALACIOS', 'E11', 2192, '30.05.1980',
42, 12, 'H', '09.07.1946', 1380 ),
( 300, 'PEDRO', 'I', 'SIERRA', 'E11', 3522, '19.06.1972',
48, 14, 'H', '27.10.1936', 1775 ),
( 310, 'MATILDE', 'F', 'SERNA', 'E11', 2130, '12.09.1964',
43, 12, 'M', '21.04.1931', 1574 ),
( 320, 'RAMON', 'V', 'MORAN', 'E21', 7112, '07.07.1965', 52,
16, 'H', '11.08.1932', 1995 ),
( 330, 'WILLY', '', 'LERMA', 'E21', 1132, '23.02.1976', 55,
14, 'H', '18.07.1941', 2537 ),
( 340, 'JAVIER', 'R', 'GIL', 'E21', 1162, '05.05.1947', 54,
16, 'H', '17.05.1926', 2384 ),
( 999, 'ANGEL', 'E', 'MALDONADO', 'E11', '----',
'22.03.1988', 69, 20, 'H', '12.11.1965', 7500 );
INSERT INTO tdepar2 ( numdep, nomdep, numdirec )
VALUES ( 'A00', 'CENTRO PROCESOS', 000010 ),
( 'B01', 'PLANIFICACION', 000020 ),
( 'C01', 'INFORMACION', 000030 ),
( 'D01', 'DESARROLLO', '' ),
( 'D11', 'FABRICACION', 000060 ),
( 'D21', 'ADMINISTRACION', 000070 ),
( 'E01', 'SERVICIOS', 000050 ),
( 'E11', 'OPERACIONES', 000090 ),
( 'E21', 'SOFTWARE', 000100 );

Consultas

1.- Obtener una lista de todas las empleadas de los departamentos que
empiecen por D y por E. La lista anterior debe contener información sobre el
número de personal, nombre, apellido y número de teléfono.

SELECT nuempl, nombre, apellido, tlfn , nomdep


FROM temple1
INNER JOIN tdepar2 ON (temple1.dept = tdepar2.numdep)
WHERE sexo='M' AND (tdepar2.nomdep LIKE 'D%' OR tdepar2.nomdep LIKE
'E%');
Rectificación con numdept en vez de nondept
SELECT nuempl, nombre, apellido, tlfn , nomdep
FROM temple1
INNER JOIN tdepar2 ON (temple1.dept = tdepar2.numdep)
WHERE sexo='M' AND (tdepar2.numdep LIKE 'D%' OR tdepar2.numdep LIKE
'E%');
2.- Obtener un listado de todos los empleados (nombre y apellido) que ganan
más de 2000€ al mes y que entraron en la compañía después del 1 de Enero de
1975. También se quiere la información correspondiente a su código de trabajo
y al número de personal de sus directores.

SELECT nombre, apellido, codtra, numdirec


FROM temple1
INNER JOIN tdepar2 ON (temple1.dept = tdepar2.numdep)
WHERE salario > 2000 AND feching > '01/01/1975';
3.- Obtener una lista con el apellido, número de departamento y salario
mensual de los empleados de los departamentos ‘A00’, ‘B01’, ‘C01’ y ‘D01’. La
salida se quiere en orden descendente de salario dentro de cada departamento.

SELECT apellido, dept, salario


FROM temple1
WHERE dept IN ( 'A00', 'B01', 'C01' , 'D01')
ORDER BY dept, salario DESC;
4.- Se pide una lista que recupere el salario medio de cada departamento junto
con el número de empleados que tiene. El resultado no debe incluir empleados
que tengan un código de trabajo mayor que 54, ni departamentos con menos
de tres empleados. Se quiere ordenada por número de departamento.

SELECT salario, numempl


FROM (
SELECT AVG(salario) as salario, COUNT(*) AS numempl, dept
FROM temple1
WHERE NOT codtra > 54
GROUP BY dept
) AS result
WHERE NOT numempl < 3
ORDER BY dept ASC;
Otra forma
SELECT dept, AVG(salario) , COUNT(*)
FROM temple1
WHERE codtra <= 54
GROUP BY dept
HAVING COUNT(*) > 2
ORDER BY dept
;
5.- Seleccionar todos los empleados de los departamentos ‘D11’ y ‘E11’ cuyo
primer apellido empiece por S.

SELECT *
FROM temple1
WHERE dept IN ( 'D11', 'E11' ) AND apellido LIKE 'S%';
6.- Obtener el nombre, apellido y fecha de ingreso de los directores de
departamento ordenados por número de personal.

SELECT nombre, apellido, feching


FROM temple1
WHERE CAST(nuempl AS INT) IN (
SELECT CAST(numdirec AS INT) FROM
tdepar2 WHERE numdirec <> ''
)
ORDER BY CAST(nuempl AS INT);
Otra forma
SELECT nuempl, nombre, apellido, feching
FROM temple1 , tdepar2
WHERE temple1.nuempl = tdepar2.numdirec
ORDER BY CAST(nuempl AS INT)
;
7.- Obtener un listado de las mujeres de los departamentos que empiecen por
D y por E cuyo nivel de educación sea superior a la media; en este caso también
ordenados por número de personal.

Ceñido al enunciado
SELECT nuempl, nombre, apellido, tlfn , nomdep
FROM temple1
INNER JOIN tdepar2 ON (temple1.dept = tdepar2.numdep)
WHERE sexo='M' AND (tdepar2.nomdep LIKE 'D%' OR tdepar2.nomdep LIKE
'E%') AND niveduc > ( SELECT AVG(niveduc) FROM temple1 )
ORDER BY nuempl;
Numdep en vez de nomdep
SELECT nuempl, nombre, apellido, tlfn , nomdep
FROM temple1
INNER JOIN tdepar2 ON (temple1.dept = tdepar2.numdep)
WHERE sexo='M' AND (tdepar2.numdep LIKE 'D%' OR tdepar2.numdep LIKE
'E%') AND niveduc > ( SELECT AVG(niveduc) FROM temple1 )
ORDER BY nuempl;
Otra forma
SELECT nuempl, nombre, apellido
FROM temple1
WHERE sexo = 'M' AND (dept LIKE 'D%' OR dept LIKE 'E%')
AND niveduc > (SELECT AVG(niveduc) FROM temple1)
ORDER BY CAST(nuempl AS INT)
;
8.- Seleccionar todos los empleados cuyo nombre sea igual al de algunas
personas del departamento D21 y cuyo código de trabajo sea diferente de
todos los del E21 (la lista debe contener el número de personal, nombre,
apellido, departamento y código de trabajo).

SELECT *
FROM temple1
WHERE nombre IN (SELECT nombre FROM temple1 WHERE dept='D21') AND
codtra NOT IN (SELECT codtra FROM temple1 WHERE dept='E21')
AND
dept <> 'D21';
SELECT nuempl, nombre, apellido, dept, codtra
FROM temple1
WHERE nombre = ANY (SELECT nombre FROM temple1
WHERE dept = 'D21')
AND codtra <> ALL ( SELECT codtra FROM temple1
WHERE dept = 'E21')
AND dept <> 'D21'
;
NOTA: Se han excluido a los del propio departamento ya que son la referencia o
patrón de comparación.
9.- Listar los empleados que no sean directores (la información que debe
aparecer es el número de personal, apellido y departamento).

SELECT *
FROM temple1
WHERE CAST(nuempl AS INT) NOT IN (SELECT CAST(numdirec AS INT) FROM
tdepar2 WHERE numdirec <> '' )
10.- Seleccionar parejas de empleados (de sexo opuesto) que hayan nacido el
mismo día (con información acerca de apellido y fecha de nacimiento).

SELECT *
FROM temple1
WHERE fechnac = (SELECT fechnac FROM temple1 WHERE sexo = 'M'
INTERSECT
SELECT fechnac FROM temple1 WHERE sexo =
'H');
11.- Obtener un listado de todos los empleados que pertenecen al mismo
departamento que Tomás Soler.

SELECT *
FROM temple1
WHERE dept = (select dept from temple1 WHERE nombre = 'Tomás' AND
apellido = 'Soler');
Rectificación Con número de departamento
SELECT *
FROM temple1
WHERE dept = (select dept from temple1 WHERE nombre = 'TOMAS' AND
apellido = 'SOLER');
Subconsultas – ejercicios
Las subconsultas permiten extraer información que incluyen varias tablas,
estas generalmente reportan un solo dato a un conjunto de resultados con una
columna. Para la parte teórica completa y las condiciones y restricciones
ver Subconsultas.

En este artículo veremos como desarrollar ejemplos analizando los datos y


armando por pasos la subconsulta.

Ejercicios
Usando Northwind

use northwind
go

Algo de teoría de Subconsultas


Pueden utilizarse de dos formas:

1. Dentro de la Lista de campos de la instrucción Select

select ListaCampos, (Select Campo from TablaSubconsulta where…) from


TablaPrincipal

2. En la clásula Where

Select ListadeCampos, OtroCampo, UltimoCampo from Tabla


Where Campo =( Select… )

1. — Listado de Clientes (Customers) que compraron en Agosto de 1997

select * from Customers


where CustomerID in
(select distinct CustomerID from Orders where Datename(mm,OrderDate)
= ‘Agosto’
and year(OrderDate) = 1997)
go

Note la subconsulta en negrita.

2. — Listado con las cantidades vendidas de los productos


descontinuados. Incluir solamente los que tienen Stock
Primero: los productos descontinuados
select * from Products where Discontinued = 1
go

¿Dónde están las unidades vendidas?


select * from [Order Details]
go

Armando la solución

select P.ProductID, P.ProductName, p.UnitPrice from Products As P


where P.Discontinued = 1 and P.UnitsInStock > 0
go
Códigos de los descontinuados que tiene Stock
select P.ProductID from Products As P where P.Discontinued = 1 and
P.UnitsInStock > 0
go

— Cantidades vendidas de cada producto


select Sum(Od.Quantity), OD.ProductID from [Order Details] As OD
where OD.ProductID in (select P.ProductID from Products
As P where P.Discontinued = 1 and P.UnitsInStock > 0)
Group by OD.ProductID
go

SOLUCION
select P.ProductID, P.ProductName, p.UnitPrice,
(select Sum(Od.Quantity) from [Order Details] As OD where OD.ProductID in
(select P.ProductID from Products As P where P.Discontinued = 1 and
P.UnitsInStock > 0)
AND od.ProductID = p.ProductID) As ‘Cantidad Vendidas’
from Products As P where P.Discontinued = 1 and P.UnitsInStock > 0
go

3. — Empleados y la cantidad de órdenes generadas y NO atendidas


Empleados
select E.EmployeeID, Empleado = E.LastName + Space(1) + E.FirstName
from Employees As E

Conteo de las Ordenes NO ATENDIDAS


select COUNT(O.OrderID) As ‘Órdenes no Atendidas’ from Orders As O where
ShippedDate is null
go

SOLUCION
select E.EmployeeID, Empleado = E.LastName + Space(1) + E.FirstName,
( — INICIO DE LA SUBCONSULTA
select COUNT(O.OrderID) from Orders As O where ShippedDate is null
AND O.EmployeeID = E.EmployeeID
) As ‘Órdenes no Atendidas’– FINAL DE LA SUBCONSULTA
from Employees As E
where (select COUNT(O.OrderID) from Orders As O where ShippedDate is null
AND O.EmployeeID = E.EmployeeID) > 0
order by ‘Órdenes no Atendidas’ desc
go
Si ya dominas las consultas SELECT de SQL con WHERE,
GROUP BY o HAVING lo siguiente que debes aprender son las
subconsultas SQL.
A continuación encontrarás el tutorial dividido en tres partes:

 Respuesta a qué son las subconsultas.


 Explicación de cómo se utilizan las subqueries en SQL.
 Varios ejemplos de subconsultas, de más fácil a más complejo.

¿Qué es una subconsulta SQL?


Primero una definición: una subconsulta es una instrucción
SELECT SQL anidada dentro de otra instrucción SELECT, INSERT,
DELETE o UPDATE. Además, debes saber que se realizarán casi
siempre dentro de la cláusula WHERE de las consultas.
Otra forma de explicarlo sería diciendo que tenemos un SELECT
dentro de otra consulta SELECT, INSERT ... INTO, UPDATE o
DELETE.
Además existen tres formas distintas de crear una subconsulta:

1. Con ANY, ALL, SOME precediendo a la consulta anidada.


2. Haciendo uso de la expresión IN o NOT IN antes de la
subconsulta.
3. EXISTS o NOT EXISTS y a continuación la subquery.

¿Cómo se usa una subconsulta SQL?


En esta parte me voy a centrar en las consultas anidadas dentro
de la cláusula WHERE, ya que las consultas INSERT INTO las expliqué
en otro artículo.
Para realizar una consulta de SQL con
subqueries necesitaremos:
 Una consulta normalmente formada del tipo SELECT, UPDATE o
DELETE
 Una cláusula WHERE o HAVING con las condiciones deseadas
más uno de los operadores SQL anteriores (ANY,
ALL,SOME,IN,NOT IN, EXISTS o NOT EXISTS) u operadores de
comparación como "=", ">" o "<"
 La consulta SELECT entre paréntesis.

Hay varias puntos importantes sobre su uso a tener en cuenta


para evitar errores:

 La subconsulta siempre irá entre paréntesis, sin excepciones.


 Las subconsultas se pueden anidar dentro de otras subconsultas.
 La subconsulta siempre deberá devolver el mismo tipo y número
de valores a comparar: no puede haber una subconsulta que
devuelva dos columnas para una comparación con 1 columna.
 La subconsulta siempre debería (aunque no es obligatorio) de
colocarse a la derecha del operador relacional.

Ahora que ya tienes la teoría rondando por tu cabeza, a


continuación te enseño un pequeño ejemplo cumpliendo los tres puntos
recién comentados.

Consulta SELECT de ejemplo


Primero voy a crear una consulta SELECT normal, que obtenga
el nombre y apellidos de las filas de una tala llamada ciudadanos

1. SELECT nombre, apellidos


2. FROM ciudadanos

Añadir la cláusula WHERE


Ahora añado la cláusula WHERE a la consulta anterior y le voy a
añadir uno de los operadores compatibles.

1. SELECT nombre, apellidos


2. FROM ciudadanos
3. WHERE dni IN ....

Subconsulta entre paréntesis


Y por último voy a realizar la subconsulta entre paréntesis que
seleccione la columna DNI de una tabla "delincuentes":

1. SELECT nombre, apellidos


2. FROM ciudadanos
3. WHERE dni IN ( SELECT dni FROM delincuentes );

¡Ya está! Lo que ocurrirá ahora es que la base de datos hará:

1. Primero resolver la subconsulta.


2. Después comparara los valores devueltos por el DNI de la
subconsulta con el dni de la consulta SELECT "PADRE".
3. Devolverá los valores de la consulta principal como resultado de
la consulta.

Ejemplos de subconsultas en SQL


Hora de los ejemplos. A partir de aquí voy a mostrarte varios
ejemplos para que acabes de interiorizar las subconsultas.
Empezaré con un par de ejemplos sencillos, siguiendo la tónica
del anterior apartado y continuaré con ejemplos más complejos y
avanzados.
Para los siguientes ejemplos voy a crear 2 tablas:

1. Tabla sobre videojuegos a la que llamaré videojuego.


2. Tabla sobre jugadores a la que llamaré jugador.

Las consultas CREATE SQL son las siguientes:

1. CREATE TABLE videojuego (


2. id INT (11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
3. titulo VARCHAR(100) NOT NULL,
4. lenguaje VARCHAR(5) NOT NULL,
5. resumen TEXT NULL,
6. nota DECIMAL (3,1),
7. total_jugadores INT DEFAULT 0
8. );
9.
10. CREATE TABLE jugador(
11. id INT (11) NOT NULL AUTO_INCREMENT,
12. nombre VARCHAR(80) NOT NULL,
13. edad INT NOT NULL,
14. es_famoso INT NOT NULL DEFAULT 0,
15. id_videojuego INT NOT NULL,
16. FOREIGN KEY(id_videojuego) REFERENCES videojuego(id),
17. fecha_registro DATE NULL
18. );

Ahora que ya tengo mis dos tablas vamos a realizar unas


subconsultas.
Ejemplo subconsultas 1
Recuperar todos los juegos en los que al menos juega 1 jugador:

1. SELECT *
2. FROM videojuego
3. WHERE id IN ( SELECT id_videojuego FROM jugador );

Otra forma alternativa de realizar esta subconsulta sería


utilizando la cláusula ANY o SOME:

1. SELECT *
2. FROM videojuego
3. WHERE id = ANY ( SELECT id_videojuego FROM jugador );
4.
5. //alternativa con SOME
6.
7. SELECT *
8. FROM videojuego
9. WHERE id = SOME ( SELECT id_videojuego FROM jugador );

Realmente las dos condiciones son iguales ya que SOME es un


ALIAS de ANY, lo que quiere decir que son el mismo comando.

Ejemplo de subqueries 2
En el siguiente ejemplo seleccionaré todos los jugadores que
juegan a un juego en Español:

1. SELECT nombre, es_famoso


2. FROM jugador
3. WHERE id_videojuego IN ( SELECT id FROM videojuego WHERE lenguaje = 'es' );

Ejemplo 3 de consultas anidadas


Ahora voy a realizar otra consulta con una subconsulta y
funciones agregadas, en concreto utilizando MAX.
Para demostrártelo selecciono los jugadores que tienen la mayor
edad entre los jugadores:

1. SELECT nombre, apellidos


2. FROM jugador
3. WHERE edad = ( SELECT MAX(edad) FROM jugador );
Ejemplo 4 subconsulta con subconsulta anidada
En este ejemplo voy a subir un poco la dificultad, voy a mostrarte
las posiblidades casi infinitas de las subqueries utilizando para ello una
subconsulta anidada dentro de otra subconsulta. Lo entenderás mejor
con el ejemplo.
Recuerda: Las subconsultas se resuelven antes que su consulta
principal

1. SELECT *
2. FROM videojuego
3. WHERE lenguaje IN ( SELECT lenguaje FROM videojuego WHERE id = SOME ( SELE
CT id_videojuego FROM jugador WHERE es_famoso = 1 ) );

¿Lo has entendido? La consulta en su totalidad selecciona todos


los videojuegos de uno de los lenguajes a los que juega algún jugador
famoso.
En orden de ejecución de las subconsultas ( de la última a la
principal) selecciono:

1. El ID de todos los jugadores famosos


2. El lenguaje de todos los videojuegos que si ID coincide con el ID
de los jugadores famosos.
3. Todos los datos de los videojuegos que su lenguaje coincide con
alguno de los idiomas de la subconsulta del punto 2.

#5 Ejemplo de subconsulta con UPDATE SQL


Creo que con las consultas SELECT se ven todas las
posibilidades de las subconsultas, pero para que veas de forma gráfica
un ejemplo de como usarlas en UPDATE o DELETE te dejo ahora un
ejemplo.
En concreto, voy a añadir 1 al contador de total_jugadores de
aquellos videojuegos que tienen un jugador jugandolo en la fecha '2019-
12-01'

1. UPDATE videojuego
2. SET total_jugadores = jugadores + 1
3. WHERE id IN ( SELECT id_videojuego FROM jugador WHERE fecha = '2019-12-01' )
Conclusión sobre la subconsultas SQL
Bueno, no he querido enrollarme más, continuaré con las
subconsultas si es necesario en otro artículo.
¡Espero que te hay asido de utilidad! Si es así, estaría genial que
dejaras un comentario con alguna duda/aporte para entre todos mejorar
el artículo. Además, si lo compartes, nos ayudarás a seguir motivados
y en crear contenido útil para programadores de todo el mundo.
¡Gracias!
https://www.srcodigofuente.es/sql-select
https://www.srcodigofuente.es/consultas-sql

También podría gustarte