Esquema y datos
CLIENTE(id_cliente, nombre, ciudad)
| id_cliente | nombre | ciudad |
|---|
| 1 | Ana | Medellín |
| 2 | Beto | Bogotá |
| 3 | Caro | Cali |
| 4 | Darío | Medellín |
PEDIDO(id_pedido, id_cliente, fecha, total)
| id_pedido | id_cliente | fecha | total |
|---|
| 100 | 1 | 2026-01-05 | 120000 |
| 101 | 1 | 2026-01-20 | 80000 |
| 102 | 2 | 2026-02-01 | 200000 |
| 103 | 3 | 2026-02-10 | 50000 |
| 104 | 1 | 2026-03-01 | 60000 |
PEDIDO.id_cliente es clave ajena hacia CLIENTE.id_cliente.
Enunciado
Escriba las siguientes consultas en SQL (MySQL):
-
Clientes frecuentes. Nombre de cada cliente que ha realizado más de un
pedido, junto con el número de pedidos y el total gastado, ordenado
de mayor a menor total. Use JOIN, GROUP BY y HAVING.
-
Clientes sin pedidos. Nombre de los clientes que nunca han hecho un
pedido. Resuélvala de dos formas equivalentes: con NOT EXISTS (subconsulta
correlacionada) y con LEFT JOIN ... IS NULL.
Indique además el resultado esperado de cada consulta sobre los datos de ejemplo.
Solución rápida — la idea clave sin formalismo
La idea clave
Consulta 1 (clientes frecuentes): junta clientes con sus pedidos, agrúpalos por
cliente y cuenta. La condición “más de un pedido” mira un conteo, así que va en
HAVING (no en WHERE, que filtra antes de agrupar).
SELECT c.nombre, COUNT(*), SUM(p.total)
FROM cliente c JOIN pedido p ON p.id_cliente = c.id_cliente
GROUP BY c.id_cliente, c.nombre
HAVING COUNT(*) > 1;
Solo Ana (3 pedidos, 260000) cumple.
Consulta 2 (clientes sin pedidos): piensa “clientes para los que no existe
ningún pedido”:
SELECT nombre FROM cliente c
WHERE NOT EXISTS (SELECT 1 FROM pedido p WHERE p.id_cliente = c.id_cliente);
Solo Darío. Regla mental: WHERE filtra filas, HAVING filtra grupos, y
NOT EXISTS/LEFT JOIN ... IS NULL sirven para “los que no tienen”.
Solución formal — lista para entregar en un parcial
1. Clientes con más de un pedido.
SELECT c.nombre, COUNT(*) AS num_pedidos, SUM(p.total) AS total_gastado
FROM cliente c
JOIN pedido p ON p.id_cliente = c.id_cliente
GROUP BY c.id_cliente, c.nombre
HAVING COUNT(*) > 1
ORDER BY total_gastado DESC;
Resultado: Ana, 3, 260000.
2. Clientes sin pedidos.
-- con NOT EXISTS
SELECT c.nombre
FROM cliente c
WHERE NOT EXISTS (SELECT 1 FROM pedido p WHERE p.id_cliente = c.id_cliente);
-- equivalente con LEFT JOIN
SELECT c.nombre
FROM cliente c
LEFT JOIN pedido p ON p.id_cliente = c.id_cliente
WHERE p.id_pedido IS NULL;
Resultado: Darío.
Explicación completa — paso a paso, con visualizaciones
1. Clientes frecuentes
Necesitamos combinar cada cliente con sus pedidos (JOIN), agrupar por cliente
(GROUP BY), quedarnos solo con los que tienen más de un pedido (HAVING) y
ordenar por lo gastado.
SELECT c.nombre,
COUNT(*) AS num_pedidos,
SUM(p.total) AS total_gastado
FROM cliente c
JOIN pedido p ON p.id_cliente = c.id_cliente
GROUP BY c.id_cliente, c.nombre
HAVING COUNT(*) > 1
ORDER BY total_gastado DESC;
Detalles importantes:
- Se usa
INNER JOIN: los clientes sin pedidos no aparecen (no hay fila que
emparejar), lo cual es correcto porque pedimos clientes que sí pidieron.
WHERE filtra filas antes de agrupar; HAVING filtra grupos después de
agrupar. Como la condición “más de un pedido” es sobre el agregado COUNT(*),
debe ir en HAVING.
- Se agrupa por
c.id_cliente, c.nombre (la clave y todo atributo no agregado del
SELECT), como exige el modo estricto de SQL.
Resultado esperado:
| nombre | num_pedidos | total_gastado |
|---|
| Ana | 3 | 260000 |
Beto y Caro tienen un solo pedido (los excluye HAVING); Darío no tiene ninguno.
2. Clientes sin pedidos
Opción A — NOT EXISTS (subconsulta correlacionada)
SELECT c.nombre
FROM cliente c
WHERE NOT EXISTS (
SELECT 1
FROM pedido p
WHERE p.id_cliente = c.id_cliente
);
La subconsulta se evalúa para cada cliente; NOT EXISTS es verdadero cuando
ese cliente no tiene ninguna fila en pedido.
Opción B — LEFT JOIN ... IS NULL
SELECT c.nombre
FROM cliente c
LEFT JOIN pedido p ON p.id_cliente = c.id_cliente
WHERE p.id_pedido IS NULL;
El LEFT JOIN conserva todos los clientes; los que no tienen pedido quedan con
las columnas de pedido en NULL. Filtrar p.id_pedido IS NULL deja exactamente
los clientes huérfanos.
Resultado esperado (ambas opciones):
Las dos consultas son equivalentes. NOT EXISTS suele expresar mejor la intención
(“no existe un pedido de este cliente”) y evita el riesgo de duplicados que tendría
un IN con subconsultas que devuelven nulos.