Maksclub
@Maksclub
maksfedorov.ru

Как упростить запрос с группировкой, максимальным значением и JOIN в MySQL?

Написал рабочий запрос, но что-то мне подсказывает, что он не в порядке...
Работает исправно, но многим не понравился двойной джойн сразу и в основном и вложенных запросах + группировка
Может чего не понимаю?

Дано
Есть 3 таблицы
- товары products
- акции discounts
упрощенная для понимания стуктура акций
5af02d9907d56583212534.png
- связь акций и товаров «многие ко многим» в таблице products_discounts

Задача
Выбрать для списка товаров акцию для каждого товара, с наибольшим приоритетом, которая включена и действующая (интервалы дат)

Сделал работающий запрос
Айдишники товаров и дату вставил для удобства сразу в запрос

Более читабельный запрос в картинке
5af02ca9d37de099273303.png
SELECT
  pd.product_id,
  d.name,
  d.discount_percent,
  d.discount_amount
FROM s_products_discount pd
LEFT JOIN s_discounts d ON pd.discount_id = d.id
LEFT JOIN (SELECT pd.product_id, MAX(d.priority) as max_priority
           FROM s_products_discount pd
           LEFT JOIN s_discounts d ON pd.discount_id = d.id
           WHERE d.visible = 1
           AND d.date_from <= '2018-05-05'
           AND d.date_to >= '2018-05-05'
           GROUP BY pd.product_id) pp ON pd.product_id = pp.product_id
WHERE 1
AND d.priority = pp.max_priority
AND pd.product_id IN ('41987', '41988', '41989', '41990', '41991', '41992', '41993', '41994')


Если своими словами, то во вложенном запросе получаю табличку, в которой все товары и и приоритетная акция, попадающая под условия
  • Вопрос задан
  • 667 просмотров
Решения вопроса 2
@Nekonyash
Попробуйте следующее:
SELECT
  pd.product_id,
  d.name,
  d.discount_percent,
  d.discount_amount
FROM s_products_discount pd
JOIN s_discounts d ON pd.discount_id = d.id
WHERE 1
AND pd.product_id IN ('41987', '41988', '41989', '41990', '41991', '41992', '41993', '41994') # проверять список ид ДО более сложной проверки
AND d.priority = (
  SELECT MAX(sd.priority)
  FROM s_products_discount spd
  JOIN s_discounts sd on spd.discount_id = sd.id
  WHERE spd.product_id = pd.product_id
  AND sd.visible = 1
  AND sd.date_from <= '2018-05-05'
  AND sd.date_to >= '2018-05-05'
)


У меня только возник вопрос... А зачем везде LEFT JOIN? Разве в текущей задаче нужен именно он, а не просто join? Такое чувство, что left пишется просто по привычке.
В чем состояла оптимизация - вы строите временную таблицу, где к каждому продукту ищется максимальный доступный приоритет. Если представить, что у нас пол миллиона продуктов, вы хотим выбрать только десяток - все равно будет построена таблица по всем продуктам. Я же переместила подзапрос в where, где будет сравниваться приоритет для конкретного продукта. Судя по моим маленьким тестам, даже на малом количестве данных запрос не проигрывает, на большом количестве данных разница должна быть заметнее.
Ответ написан
LaRN
@LaRN
Senior Developer
В подзапросе таблица s_products_discount не нужна, можно вот так попробовать:

SELECT pd.product_id,
       d.name,
       d.discount_percent,
       d.discount_amount
  FROM s_products_discount pd
  LEFT JOIN s_discounts     d
         ON d.id = pd.discount_id 
  LEFT JOIN (SELECT MAX(d.priority) as max_priority
               FROM s_discounts dd
              WHERE dd.id         = pd.discount_id
                AND dd.visible    = 1
                AND dd.date_from <= '2018-05-05'
                AND dd.date_to   >= '2018-05-05') pp 
         ON pp.max_priority = d.priority
 WHERE pd.product_id IN ('41987', '41988', '41989', '41990', '41991', '41992', '41993', '41994')
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы