Muchas tablas, y una tabla unida consigo misma
En resumen: Varios JOIN seguidos enlazan una cadena de tablas, y un self join enlaza una tabla consigo misma, por ejemplo una categoría con su categoría padre.
Problema
Facturación por categoría
La dueña quiere saber de qué categorías viene el dinero. La cantidad y el precio están en order_items. Si el pedido se canceló está en orders. La categoría del producto está en products, y el nombre de la categoría en categories.
Cuatro tablas para un informe. JOIN no se detiene en dos.
La idea
Una cadena de JOIN
Cada JOIN siguiente añade una tabla a lo que ya se ha armado. El pedido 60, con sus productos y las categorías de estos:
El camino sigue las claves foráneas: orders → order_items (por order_id) → products (por product_id) → categories (por category_id). Cada eslabón es un JOIN … ON aparte.
Tanto products como categories tienen una columna name. Por eso aquí les ponemos nombre con AS: product y category. Si no, el resultado tendría dos columnas name.
Pruébalo
Quién valoró qué
Para cada reseña, muestra el nombre del cliente, el nombre del producto y la puntuación.
Solución
SELECT cu.name AS customer, p.name AS product, r.rating
FROM reviews r
JOIN customers cu ON cu.id = r.customer_id
JOIN products p ON p.id = r.product_id;FROM reviews r JOIN customers cu ON cu.id = r.customer_id JOIN products p ON p.id = r.product_id. 16 filas. La reseña está en el medio: apunta tanto al cliente como al producto. El café en grano tiene tres reseñas: 5, 4 y 1.
Míralo
El dinero perdido
Facturación por categoría, sin los pedidos cancelados:
Parece correcto. Ahora la facturación total, sin categorías:
Las seis categorías dan 1537 €, pero la facturación es de 1697 €.
En una cadena de JOIN, cada eslabón puede echar filas sin avisar. Para un producto sin categoría no hay pareja en categories. Con un LEFT JOIN a las categorías las ventas se quedan, y COALESCE le da un nombre a la categoría que falta:
Siete filas, y el total es de 1697 €. Siempre que un informe cuente dinero, compara su total con el total general. La diferencia muestra las filas perdidas.
Pruébalo
Facturación por ciudad
Muestra la facturación por ciudad del cliente, sin los pedidos cancelados.
Solución
SELECT c.city, SUM(oi.quantity * oi.unit_price) AS revenue
FROM orders o
JOIN customers c ON c.id = o.customer_id
JOIN order_items oi ON oi.order_id = o.id
WHERE o.status <> 'cancelled'
GROUP BY c.city;FROM orders o JOIN customers c ON c.id = o.customer_id JOIN order_items oi ON oi.order_id = o.id, WHERE o.status <> 'cancelled' y GROUP BY c.city. Siete filas: Sofía lidera con 658,70 €. La fila con NULL es Boris, que no tiene ciudad: 66,60 €. Aquí no se pierde nada, porque cada pedido tiene un cliente.
La idea
Una tabla unida consigo misma
En las categorías, parent_id apunta a id de la misma tabla. Para ver una categoría junto a su padre, unes categories consigo misma. La tabla participa dos veces, con dos nombres cortos distintos:
c es la categoría, parent es la misma tabla vista como el padre. ON parent.id = c.parent_id dice: el padre es la fila cuyo id es igual al parent_id de la categoría. Esto se llama self join. Aquí los nombres cortos son obligatorios: sin ellos no se puede distinguir name de las dos copias.
Es un LEFT JOIN para que «Alimentos» y «Hogar», que no tienen padre, se queden.
Ejercicio
Dos tareas
1. El nuevo catálogo necesita los 25 productos con su categoría y su categoría padre. La tarjeta de regalo tiene que estar también en la lista.
El producto, su categoría y la categoría padre, para todos los productos:
Solución
SELECT p.name AS product, cat.name AS category, parent.name AS parent
FROM products p
LEFT JOIN categories cat ON cat.id = p.category_id
LEFT JOIN categories parent ON parent.id = cat.parent_id;FROM products p LEFT JOIN categories cat ON cat.id = p.category_id LEFT JOIN categories parent ON parent.id = cat.parent_id. 25 filas. Los dos JOIN deben ser LEFT: si el segundo es un JOIN normal, la tarjeta de regalo se descarta otra vez, porque una categoría NULL no tiene padre.
2. ¿Cuál es la facturación de las dos categorías de arriba, «Alimentos» y «Hogar»? Sin los pedidos cancelados.
Facturación por categoría de arriba:
Solución
SELECT parent.name AS parent, SUM(oi.quantity * oi.unit_price) AS revenue
FROM order_items oi
JOIN orders o ON o.id = oi.order_id
JOIN products p ON p.id = oi.product_id
JOIN categories cat ON cat.id = p.category_id
JOIN categories parent ON parent.id = cat.parent_id
WHERE o.status <> 'cancelled'
GROUP BY parent.name;A la cadena del informe por categoría, añade JOIN categories parent ON parent.id = cat.parent_id y agrupa por parent.name. «Alimentos» 824,60 €, «Hogar» 712,40 €. Juntas suman 1537 €: las tarjetas de regalo, 160 €, no están en ninguna categoría de arriba.
Más precisamente, para curiosos
El orden de los JOIN normales en una consulta no cambia el resultado: la base misma elige con qué tabla empezar. Con LEFT JOIN el orden importa, porque «izquierda» es todo lo que se ha armado antes.
Una tabla unida consigo misma da solo un nivel hacia arriba. Un árbol con muchos niveles (una subcategoría de una subcategoría) se recorre con WITH RECURSIVE, en el módulo de subconsultas.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: GROUP BY: un informe por grupos · Clave primaria y clave foránea · JOIN: dos tablas en una consulta · LEFT JOIN: también las filas sin pareja
- Continúa en: UNION, INTERSECT, EXCEPT y CROSS JOIN · Un informe por pasos: WITH · Árboles y secuencias: WITH RECURSIVE · Vistas
- Ejercicios sobre el tema: Unir tablas