Объединение таблиц в 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.
Истории из жизни айти и обсуждение кода.