Много таблици и таблица със себе си
Накратко: Няколко JOIN един след друг свързват верига от таблици, а self join свързва таблица със самата нея, например категорията с родителската ѝ категория.
Проблем
Оборотът по категории
Собственичката иска да знае от кои категории идват парите. Количеството и цената са в order_items. Дали поръчката е отказана, пише в orders. Категорията на продукта е в products, а името на категорията в categories.
Четири таблици за един отчет. JOIN не спира на две.
Идеята
Верига от JOIN
Всеки следващ JOIN добавя таблица към това, което вече е събрано. Поръчка 60, с продуктите и категориите им:
Пътят е по външните ключове: orders → order_items (по order_id) → products (по product_id) → categories (по category_id). Всяка връзка е отделен JOIN … ON.
И products, и categories имат колона name. Затова тук им даваме имена с AS: product и category. Иначе в резултата щеше да има две колони name.
Пробвай
Кой какво е оценил
Покажи за всеки отзив името на клиента, името на продукта и оценката.
Решение
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 реда. Отзивът е в средата: той сочи и към клиента, и към продукта. Кафето на зърна има три отзива: 5, 4 и 1.
Виж го
Изгубените пари
Оборотът по категории, без отказаните поръчки:
Изглежда наред. Сега целият оборот, без категории:
Шестте категории дават 1 537 €, а оборотът е 1 697 €.
Във верига от JOIN всяка връзка може тихо да изхвърли редове. За продукт без категория двойка в categories няма. С LEFT JOIN към категориите продажбите остават, а COALESCE дава име на липсващата категория:
Седем реда и сборът е 1 697 €. Щом отчетът брои пари, сравни сбора му с общия. Разликата показва изгубените редове.
Пробвай
Оборотът по градове
Покажи оборота по град на клиента, без отказаните поръчки.
Решение
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' и GROUP BY c.city. Седем реда: София води с 658,70 €. Редът с NULL е Борис, който е без град: 66,60 €. Тук нищо не се губи, защото всяка поръчка има клиент.
Идеята
Таблица, свързана със себе си
В категориите parent_id сочи към id в същата таблица. За да видиш категорията до родителската ѝ, свързваш categories със себе си. Таблицата влиза два пъти, с две различни кратки имена:
c е категорията, parent е същата таблица, видяна като родител. ON parent.id = c.parent_id казва: родителят е редът, чието id е равно на parent_id на категорията. Това се казва self join. Тук кратките имена са задължителни: без тях name от двете копия не се различава.
LEFT JOIN е, за да останат „Храни“ и „Дом“, които нямат родител.
Упражнение
Две задачи
1. За новия каталог трябват всички 25 продукта с категорията и родителската категория. Подаръчната карта също трябва да е в списъка.
Продуктът, категорията и родителската категория, за всички продукти:
Решение
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 реда. И двата JOIN трябва да са LEFT: ако вторият е обикновен JOIN, подаръчната карта отпада отново, защото за NULL категория няма родител.
2. Колко е оборотът на двете главни категории, „Храни“ и „Дом“? Без отказаните поръчки.
Оборотът по главна категория:
Решение
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;Към веригата от отчета по категории добавяш JOIN categories parent ON parent.id = cat.parent_id и групираш по parent.name. „Храни“ 824,60 €, „Дом“ 712,40 €. Заедно са 1 537 €: подаръчните карти за 160 € не са в никоя главна категория.
По-точно, за любопитните
Редът на JOIN в една заявка не променя резултата на обикновения JOIN: базата сама избира от коя таблица да започне. При LEFT JOIN редът е важен, защото „лява“ е всичко, събрано преди него.
Таблица, свързана със себе си, дава само едно ниво нагоре. Дърво с много нива (подкатегория на подкатегория) се обхожда с WITH RECURSIVE, в модула за подзаявките.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: GROUP BY: отчет по групи · Първичен и външен ключ · JOIN: две таблици в една заявка · LEFT JOIN: и редовете без двойка
- Продължава в: UNION, INTERSECT, EXCEPT и CROSS JOIN · Отчет на стъпки: WITH · Дървета и редици: WITH RECURSIVE · Изгледи
- Задачи по темата: Свързване на таблици