En esta clase vamos a practicar consultas SQL usando la base de ejemplo de empresa: empleados, supervisores, clientes, tipos de cliente, productos, categorías, ventas y detalle de ventas.
El objetivo es reforzar consultas que combinan varias tablas y que responden preguntas reales de un sistema. No se trata solamente de escribir SQL largo, sino de entender qué información necesitamos, de qué tablas sale y cómo se relacionan esas tablas.
INNER JOIN,
LEFT JOIN, WHERE, LIKE, GROUP BY,
COUNT, SUM, AVG, HAVING,
ORDER BY y subconsultas.
COALESCE, porque todavía no se están repasando aquí.
Si una suma o promedio no tiene datos relacionados, MySQL puede mostrar NULL.
Eso significa que no hubo registros relacionados para calcular ese dato.
Los ejercicios usan la base:
empresa_sql_avanzado_1
Y las tablas principales son:
Este diagrama muestra la estructura general de la base de datos de práctica. Cada cuadro representa una tabla. Los campos marcados como PK son llaves primarias y los campos marcados como FK son llaves foráneas.
JOIN se vuelven adivinanzas.
ventas y los productos vendidos están en venta_detalles.
Resuelve los siguientes ejercicios en DBeaver o en el gestor de base de datos. Son ejercicios nuevos, pero usan los mismos conceptos que ya repasamos. Intenta resolverlos primero sin ver la respuesta.
Mostrar el nombre del vendedor, la ciudad del cliente, cuántas ventas cerradas hizo en esa ciudad y cuánto vendió en total.
Ordena el resultado por vendedor y después por total vendido de mayor a menor.
ventas, empleados y clientes.
Agrupa por vendedor y ciudad.
Mostrar cada categoría de producto, cuántas unidades se vendieron y cuánto dinero generó.
Solo deben aparecer categorías cuyo total vendido sea mayor a 5000.
categorias_producto, productos y venta_detalles.
Para filtrar después de agrupar usa HAVING.
Mostrar los productos cuyo precio sea mayor al precio promedio de los productos de su misma categoría.
El resultado debe mostrar categoría, código, producto, precio y promedio de su categoría.
categoria_id
del producto que se está revisando.
Mostrar los clientes que no tienen ninguna venta cerrada.
El resultado debe mostrar tipo de cliente, nombre del cliente y ciudad.
LEFT JOIN entre clientes y ventas.
Después filtra donde no exista venta relacionada.
Antes de revisar las respuestas, es importante recordar que las partes de una consulta SQL tienen un orden. Si cambiamos ese orden, MySQL puede marcar error aunque la idea de la consulta sea correcta.
WHERE filtra filas normales antes de hacer grupos.HAVING filtra grupos después de usar GROUP BY.ORDER BY ordena el resultado final.
SELECT
e.nombre AS vendedor,
COUNT(v.id) AS cantidad_ventas,
SUM(v.total) AS total_vendido
FROM ventas v
INNER JOIN empleados e ON e.id = v.vendedor_id
WHERE v.estado = 'cerrada'
GROUP BY
e.id,
e.nombre
HAVING COUNT(v.id) > 2
ORDER BY total_vendido DESC;
Lectura de la consulta:
SELECT
e.nombre AS vendedor,
COUNT(v.id) AS cantidad_ventas
FROM ventas v
INNER JOIN empleados e ON e.id = v.vendedor_id
WHERE v.estado = 'cerrada'
HAVING COUNT(v.id) > 2
GROUP BY
e.id,
e.nombre;
HAVING necesita que los grupos ya existan.
Primero debe ir GROUP BY y después HAVING.
SELECT
e.nombre AS vendedor,
SUM(v.total) AS total_vendido
FROM ventas v
INNER JOIN empleados e ON e.id = v.vendedor_id
WHERE SUM(v.total) > 10000
GROUP BY
e.id,
e.nombre;
WHERE ocurre antes de agrupar.
En ese momento todavía no existe el resultado de SUM(v.total).
Para filtrar por una suma, se usa HAVING.
SELECT
e.nombre AS vendedor,
SUM(v.total) AS total_vendido
FROM ventas v
INNER JOIN empleados e ON e.id = v.vendedor_id
GROUP BY
e.id,
e.nombre
HAVING SUM(v.total) > 10000
ORDER BY total_vendido DESC;
WHERE.HAVING.
Las soluciones se pueden mostrar con contraseña.
SELECT
e.nombre AS vendedor,
c.ciudad,
COUNT(v.id) AS ventas_cerradas,
SUM(v.total) AS total_vendido
FROM ventas v
INNER JOIN empleados e ON e.id = v.vendedor_id
INNER JOIN clientes c ON c.id = v.cliente_id
WHERE v.estado = 'cerrada'
GROUP BY
e.id,
e.nombre,
c.ciudad
ORDER BY
e.nombre ASC,
total_vendido DESC;
ventas porque queremos analizar ventas cerradas.
Con INNER JOIN se obtiene el nombre del vendedor y la ciudad del cliente.
Luego se agrupa por vendedor y ciudad para calcular cantidad de ventas y total vendido.
SELECT
cp.nombre AS categoria,
SUM(vd.cantidad) AS unidades_vendidas,
SUM(vd.total_linea) AS total_generado
FROM categorias_producto cp
INNER JOIN productos p ON p.categoria_id = cp.id
INNER JOIN venta_detalles vd ON vd.producto_id = p.id
GROUP BY
cp.id,
cp.nombre
HAVING SUM(vd.total_linea) > 5000
ORDER BY total_generado DESC;
HAVING y no WHERE.
SELECT
cp.nombre AS categoria,
p.codigo,
p.nombre AS producto,
p.precio,
(
SELECT AVG(p2.precio)
FROM productos p2
WHERE p2.categoria_id = p.categoria_id
) AS promedio_categoria
FROM productos p
INNER JOIN categorias_producto cp ON cp.id = p.categoria_id
WHERE p.precio > (
SELECT AVG(p2.precio)
FROM productos p2
WHERE p2.categoria_id = p.categoria_id
)
ORDER BY
cp.nombre ASC,
p.precio DESC;
SELECT
tc.nombre AS tipo_cliente,
c.nombre AS cliente,
c.ciudad
FROM clientes c
INNER JOIN tipos_cliente tc ON tc.id = c.tipo_cliente_id
LEFT JOIN ventas v
ON v.cliente_id = c.id
AND v.estado = 'cerrada'
WHERE v.id IS NULL
ORDER BY
tc.nombre ASC,
c.nombre ASC;
LEFT JOIN permite listar clientes aunque no tengan ventas cerradas.
Cuando no hay venta relacionada, v.id queda vacío.
Por eso usamos WHERE v.id IS NULL para quedarnos con clientes sin compras cerradas.
Antes de revisar las respuestas, es importante recordar que las partes de una consulta SQL tienen un orden. Si cambiamos ese orden, MySQL puede marcar error aunque la idea de la consulta sea correcta.
WHERE filtra filas normales antes de hacer grupos.HAVING filtra grupos después de usar GROUP BY.ORDER BY ordena el resultado final.
SELECT
e.nombre AS vendedor,
COUNT(v.id) AS cantidad_ventas,
SUM(v.total) AS total_vendido
FROM ventas v
INNER JOIN empleados e ON e.id = v.vendedor_id
WHERE v.estado = 'cerrada'
GROUP BY
e.id,
e.nombre
HAVING COUNT(v.id) > 2
ORDER BY total_vendido DESC;
Lectura de la consulta:
SELECT
e.nombre AS vendedor,
COUNT(v.id) AS cantidad_ventas
FROM ventas v
INNER JOIN empleados e ON e.id = v.vendedor_id
WHERE v.estado = 'cerrada'
HAVING COUNT(v.id) > 2
GROUP BY
e.id,
e.nombre;
HAVING necesita que los grupos ya existan.
Primero debe ir GROUP BY y después HAVING.
SELECT
e.nombre AS vendedor,
SUM(v.total) AS total_vendido
FROM ventas v
INNER JOIN empleados e ON e.id = v.vendedor_id
WHERE SUM(v.total) > 10000
GROUP BY
e.id,
e.nombre;
WHERE ocurre antes de agrupar.
En ese momento todavía no existe el resultado de SUM(v.total).
Para filtrar por una suma, se usa HAVING.
SELECT
e.nombre AS vendedor,
SUM(v.total) AS total_vendido
FROM ventas v
INNER JOIN empleados e ON e.id = v.vendedor_id
GROUP BY
e.id,
e.nombre
HAVING SUM(v.total) > 10000
ORDER BY total_vendido DESC;
WHERE.HAVING.
En este repaso practicamos consultas que combinan varias ideas: relaciones, filtros, agrupaciones, cálculos y subconsultas.