Мне нужен запрос, который будет выбирать только одно (GROUP BY phi.id_product
) изображение для каждого продукта, и это изображение должно быть тем, у кого наивысший приоритет (внутренний SELECT
с инструкцией ORDER BY
).
Приоритет сохраняется в таблице отношений N: M, называемой product_has_image
Я создал запрос, но для выполнения потребовалось около 3 секунд, и мне нужно его оптимизировать. Вот он:
SELECT p.*, i.id AS imageid
FROM `product` p JOIN `category` c on c.`id` = p.`id_category`
LEFT OUTER JOIN (SELECT id_product, id_image FROM
`product_has_image` ORDER BY priority DESC) phi ON p.id = phi.id_product
LEFT OUTER JOIN `image` i ON phi.id_image = i.id
WHERE (c.`id_parent` = 2 OR c.`id` = 2)
GROUP BY phi.id_product
Индексы, которые я считаю важными в этом запросе, следующие:
image (PRIMARY id)
product_has_image (PRIMARY id_product, id_image; INDEX id_product; INDEX id_image)
product (PRIMARY id, id_category; INDEX id_category)
category (PRIMARY id; INDEX id_parent)
Большую часть времени требуется связать таблицы с помощью инструкции SELECT, необходимой для сортировки.
Соединение с LEFT JOIN [product_has_image] phi ON p.id = phi.id_product
выполняется намного быстрее, но не назначает изображение с наивысшим приоритетом.
Любая помощь будет оценена.
Переформатирован для чувствительности.,.
SELECT p.*, i.id AS imageid
FROM `product` p
INNER JOIN `category` c on (c.`id` = p.`id_category`)
LEFT OUTER JOIN (SELECT id_product, id_image
FROM `product_has_image`
ORDER BY priority DESC) phi
ON (p.id = phi.id_product)
LEFT OUTER JOIN `image` i
ON (phi.id_image = i.id)
WHERE (c.`id_parent` = 2 OR c.`id` = 2)
GROUP BY phi.id_product
Не видя плана выполнения или DDL, я бы догадался (содрогнулся), что проблема, вероятно, будет внутренней select/sort. Если вы создаете представление
create view highest_priority_images as
select id_product, max(priority)
from product_has_image
group by id_product
Затем вы можете заменить этот внутренний SELECT... ORDER BY с помощью SELECT... INNER JOIN на этом представлении. Это уменьшит мощность, поэтому я ожидаю, что он будет работать быстрее.
Проводка DDL поможет.
Я бы попытался сделать это вот так:
SELECT p.*, i.id AS imageid
FROM `product` p
INNER JOIN `category` c ON c.id = p.id_category
/* a list of `id_product`s with their highest priorities
from `product_has_image` */
LEFT OUTER JOIN (
SELECT id_product, MAX(priority) AS max_priority
FROM `product_has_image`
GROUP BY id_product
) m ON p.id = m.id_product
/* now joining `product_has_image` again, using
m.`max_priority` for additional filtering */
LEFT OUTER JOIN `product_has_image` phi
ON p.id = phi.id_product AND m.max_priority = phi.priority
/* if you only select `id` from `image`, you can use
phi.`id_image` instead and remove this join */
LEFT OUTER JOIN `image` i ON phi.id_image = i.id
WHERE c.id_parent = 2 OR c.id = 2
Не удается проверить его сейчас, но не возможно ли это сделать?
SELECT p.*, i.id AS imageid
FROM `product` p JOIN `category` c on c.`id` = p.`id_category`
LEFT JOIN `product_has_image` phi ON p.id = phi.id_product
LEFT OUTER JOIN `image` i ON phi.id_image = i.id
WHERE (c.`id_parent` = 2 OR c.`id` = 2)
GROUP BY phi.id_product
ORDER BY phi.priority DESC
Сделайте это в регулярном соединении и порядке по phi.priority.