Объединение таблиц в MySql. JOIN
По возможности стараюсь не использовать join-ы из-за медленной их работы. Перекрестные и вложенные запросы отрабатывают быстрее и, на мой взгляд, читабельней. Но бывают ситуации, когда без join-ов не обойтись. Оставляю на заметку.
Задача: есть интернет-магазин с товарами.
Структура таблиц:
goods: id, good, price - сами товары
goods_description: id, good_id, description - описания товаров
goods_discount: id, good_id, discount_price - скидки на товары
Причин, по которым мы выделяем описания и скидки в отдельные товары, может быть много.
Чаще всего к этому приходим в процессе нормализации,
когда по максимуму избавляемся от избыточности данных и когда не хотим хранить лишние пустые строки в таблице товаров.
В общем, нам нужно получить сводную таблицу - список товаров с описаниями и скидками, если они есть.
Обычными перекрестными запросами здесь не обойтись, потому что в таблицах описаний и скидок, могут быть далеко не все id товаров.
Вот здесь нам и поможет join.
Для начала создадим таблицы и заполним их тестовыми данными:
Структура таблиц:
-- Описание для таблицы goods DROP TABLE IF EXISTS goods; CREATE TABLE goods ( id INT(10) UNSIGNED NOT NULL, good VARCHAR(255) NOT NULL, price INT(11) NOT NULL, PRIMARY KEY (id) ) ENGINE = INNODB AVG_ROW_LENGTH = 4096 CHARACTER SET utf8 COLLATE utf8_general_ci; -- Описание для таблицы goods_description DROP TABLE IF EXISTS goods_description; CREATE TABLE goods_description ( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, good_id INT(10) UNSIGNED NOT NULL, description TEXT NOT NULL, PRIMARY KEY (id) ) ENGINE = INNODB AUTO_INCREMENT = 3 AVG_ROW_LENGTH = 8192 CHARACTER SET utf8 COLLATE utf8_general_ci; -- Описание для таблицы goods_discount DROP TABLE IF EXISTS goods_discount; CREATE TABLE goods_discount ( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, good_id INT(10) UNSIGNED NOT NULL, discount_price INT(11) NOT NULL, PRIMARY KEY (id) ) ENGINE = INNODB AUTO_INCREMENT = 3 AVG_ROW_LENGTH = 8192 CHARACTER SET utf8 COLLATE utf8_general_ci;
Тестовые данные:
-- Вывод данных для таблицы goods INSERT INTO goods VALUES (1, 'Ноутбук', 30000), (2, 'Телефон', 5000), (3, 'Смартфон', 10000), (4, 'Планшет', 15000); -- Вывод данных для таблицы goods_description INSERT INTO goods_description VALUES (1, 1, 'Отличный ноутбук'), (2, 2, 'Хороший телефон'); -- Вывод данных для таблицы goods_discount INSERT INTO goods_discount VALUES (1, 1, 29200), (2, 3, 9500);
А теперь сам запрос:
В нашем случае нужно, чтобы вывелись все товары. Те, у которых нет описания и скидок, в соответствующих полях должны показать null. В этом случае мы будем использовать left join
SELECT g.id as good_id, g.good as good, g.price as price, g_descr.description as description, g_dis.discount_price AS discount_price FROM goods as g LEFT JOIN goods_description AS g_descr ON g.id=g_descr.good_id LEFT JOIN goods_discount AS g_dis ON g.id=g_dis.good_id order by g.id
Этот запрос выведет таблицу, у которой в недостающих полях будут null-ы. Если мы хотим получить записи, данные для которых есть во всех трех таблицах, то нужно использовать inner join (просто замените left на inner в запросе и увидите разницу). Оператор right join выполняет присоединение таблиц "справа налево". Чтобы понять разницу, просто замените left на right и по результатам станет понятно, в чем их отличие.
Таким образом, мы увидели, что перекрестным запросом можно заменить только оператор inner join.
Истории из жизни айти и обсуждение кода.