Объединение таблиц в MySql. JOIN

июнь 3 , 2015
Метки:

По возможности стараюсь не использовать 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.

Метки:
Заходите в группу в контакте - https://vk.com/webdevkin
Анонсы статей, обсуждения интернет-магазинов, vue, фронтенда, php, гита.
Истории из жизни айти и обсуждение кода.
Как Вам статья? Оцените!