Contenido de esta guía
Cómo practicar con estos ejercicios
Estos ocho ejercicios SQL resueltos avanzan desde filtros hasta consultas con JOIN y agrupaciones. Todos usan la base ficticia TiendaSQL del sitio y se han ejecutado sobre el archivo SQLite descargable. Intenta cada reto antes de abrir su solución; compara las filas devueltas y luego modifica la consulta.
Abre el Editor SQL para ejecutar las consultas en el navegador. También puedes descargar la base SQLite y usarla en tu equipo. El editor carga la misma base educativa. No necesitas una cuenta.
Ejercicios SQL con solución
Usaremos cuatro tablas: clientes (id, nombre, ciudad, activo), productos (id, nombre, precio, stock), pedidos (id, cliente_id, estado, total) y detalle_pedido (pedido_id, producto_id, cantidad). Cada resultado esperado corresponde a la versión actual de TiendaSQL.
1. Filtra clientes activos de Madrid
Tu reto
Muestra el nombre y la ciudad de los clientes activos de Madrid, ordenados por nombre. En esta base, activo = 1 significa que el cliente está activo.
Antes de ejecutar: predice cuántas filas devolverá tu consulta.
Ver solución y resultado esperado
SELECT nombre, ciudad
FROM clientes
WHERE activo = 1 AND ciudad = 'Madrid'
ORDER BY nombre;Resultado comprobado: 3 filas: Ana Torres, Marta Ruiz y Noa Vidal (todas de Madrid).
Primero filtra las filas; después ordena las que cumplen ambas condiciones. Sin ORDER BY, el orden no está garantizado.
Prueba una variante: Cambia Madrid por Valencia y comprueba cuántos clientes activos hay.
2. Encuentra productos con poco stock
Tu reto
Lista nombre y stock de los productos con menos de 10 unidades. Ordena primero por stock ascendente y después por nombre.
Antes de ejecutar: predice cuántas filas devolverá tu consulta.
Ver solución y resultado esperado
SELECT nombre, stock
FROM productos
WHERE stock < 10
ORDER BY stock ASC, nombre ASC;Resultado comprobado: 6 filas. Los dos primeros son Altavoz y Portátil Pro, ambos con stock 0; el último es Disco SSD con 9.
El segundo criterio resuelve el empate de stock cero. El filtro < 10 también incluye cero.
Prueba una variante: Cambia el umbral a stock <= 4 y predice qué productos quedan.
3. Cuenta las ciudades distintas
Tu reto
Obtén una lista alfabética de ciudades de clientes sin duplicados. Devuelve solo la columna ciudad.
Antes de ejecutar: predice cuántas filas devolverá tu consulta.
Ver solución y resultado esperado
SELECT DISTINCT ciudad
FROM clientes
ORDER BY ciudad;Resultado comprobado: 6 ciudades: A Coruña, Bilbao, Madrid, Sevilla, Valencia y Zaragoza.
DISTINCT elimina repeticiones de la columna seleccionada; no cuenta clientes. Si quieres contar ciudades, envuelve esta idea en COUNT(DISTINCT ciudad).
Prueba una variante: Escribe la consulta que devuelva solo el número de ciudades distintas.
4. Muestra los tres productos más caros
Tu reto
Devuelve nombre y precio de los tres productos con mayor precio. Usa el id para desempatar precios iguales.
Antes de ejecutar: predice cuántas filas devolverá tu consulta.
Ver solución y resultado esperado
SELECT nombre, precio
FROM productos
ORDER BY precio DESC, id
LIMIT 3;Resultado comprobado: Portátil Pro (999), Monitor 27 (249) y Monitor 24 (179), en ese orden.
DESC coloca primero el precio más alto. En SQLite, LIMIT 3 conserva tres filas; SQL Server usaría TOP (3).
Prueba una variante: Muestra los tres más baratos cambiando el sentido del orden.
5. Resume pedidos por estado
Tu reto
Cuenta cuántos pedidos hay en cada estado. Ordena por cantidad descendente y, para empates, por nombre del estado.
Antes de ejecutar: predice cuántas filas devolverá tu consulta.
Ver solución y resultado esperado
SELECT estado, COUNT(*) AS cantidad
FROM pedidos
GROUP BY estado
ORDER BY COUNT(*) DESC, estado;Resultado comprobado: enviado: 9; pendiente: 6; cancelado: 3.
Cada grupo reúne pedidos con el mismo estado. COUNT(*) cuenta las filas de ese grupo y la ordenación compara el tamaño de los grupos.
Prueba una variante: Filtra con HAVING COUNT(*) >= 6 antes de ordenar.
6. Resume pedidos e importes por cliente
Tu reto
Muestra los tres clientes con mayor suma de pedidos.total, sin excluir estados. Devuelve nombre, número de pedidos y total redondeado a dos decimales.
Antes de ejecutar: predice cuántas filas devolverá tu consulta.
Ver solución y resultado esperado
SELECT c.nombre,
COUNT(p.id) AS pedidos,
ROUND(SUM(p.total), 2) AS total
FROM clientes AS c
JOIN pedidos AS p ON p.cliente_id = c.id
GROUP BY c.id, c.nombre
ORDER BY SUM(p.total) DESC, c.id
LIMIT 3;Resultado comprobado: Marta Ruiz: 3 pedidos y 1214,90; Sara Gómez: 2 y 1112,00; Ana Torres: 3 y 402,30.
El JOIN conecta cada pedido con su cliente; agrupar por id evita mezclar personas que podrían compartir nombre. Se suman todos los estados, incluidos pedidos cancelados, así que este importe no equivale a ingresos efectivos. El editor puede mostrar 1214.9: es el mismo valor numérico que 1214,90; ROUND no fija el formato visual.
Prueba una variante: Añade WHERE p.estado != 'cancelado' antes de GROUP BY y observa el cambio.
7. Localiza clientes sin pedidos
Tu reto
Devuelve los nombres de clientes para los que no existe ninguna fila en pedidos, en orden alfabético.
Antes de ejecutar: predice cuántas filas devolverá tu consulta.
Ver solución y resultado esperado
SELECT c.nombre
FROM clientes AS c
LEFT JOIN pedidos AS p ON p.cliente_id = c.id
WHERE p.id IS NULL
ORDER BY c.nombre;Resultado comprobado: 2 filas: Mario Gil y Pablo Sanz.
El LEFT JOIN conserva a todos los clientes. Cuando falta un pedido correspondiente, las columnas de p son NULL; el filtro selecciona esos casos.
Prueba una variante: Resuelve el mismo reto con NOT EXISTS y compara los resultados.
8. Cuenta unidades por producto en los pedidos
Tu reto
Suma detalle_pedido.cantidad para cada producto y muestra los tres con más unidades. En empates, ordena por id de producto.
Antes de ejecutar: predice cuántas filas devolverá tu consulta.
Ver solución y resultado esperado
SELECT pr.nombre, SUM(d.cantidad) AS unidades
FROM productos AS pr
JOIN detalle_pedido AS d ON d.producto_id = pr.id
GROUP BY pr.id, pr.nombre
ORDER BY SUM(d.cantidad) DESC, pr.id
LIMIT 3;Resultado comprobado: Cable USB-C: 6 unidades; Ratón: 4; Auriculares: 4.
La pregunta es por unidades, por eso se usa SUM(cantidad) en lugar de COUNT(*). Aquí se incluyen detalles de pedidos cancelados porque no se ha unido ni filtrado la tabla pedidos.
Prueba una variante: Une también pedidos y excluye los cancelados; explica por qué cambia el ranking.
Después de resolverlos
Si fallaste en los primeros cuatro, repasa SELECT, WHERE y orden y límites. Para los últimos, practica GROUP BY, INNER JOIN y LEFT JOIN. Después prueba el proyecto de análisis de ventas sin mirar las soluciones.
Los ejemplos están escritos para SQLite. PostgreSQL y MySQL comparten gran parte de esta sintaxis; en SQL Server cambia LIMIT por TOP u OFFSET ... FETCH, según la consulta.