Оптимизация SQL запроса с GROUP BY и подзапросом
Интересует, есть ли способы оптимизировать такой запрос в MySQL. Цель: возвратить список отсортированных товаров по популярности, при этом оставить только те товары, которые есть в наличии в конкретных складах и которые имеют цену.
| Таблица | Описание | Поля |
|---|---|---|
| products | Таблица с главной информацией о товаре | |
| products_prices | Таблица с ценами товаров | - product_id - organization_id - price То есть у товара может быть несколько цен |
| product_popularity | Таблица, которая хранит информацию об очках популярности | - product_id (primary key) - total |
| warehouses_products_amount | Таблица, которая хранит информацию о кол-ве товаров на складах | - product_id - warehouse_id - amount Primary key: product_id+warehouse_id |
Запрос:
SELECT products.product_id
, popularity.total as popularity
, min(prices.price)
FROM products
LEFT JOIN product_popularity as popularity ON
popularity.product_id = products.product_id
LEFT JOIN product_prices as prices ON
prices.product_id = products.product_id
WHERE
popularity.total >= 1
AND products.status IN ('A')
AND prices.organization_id IN (10129, 25195)
AND
(select 1
from warehouses_products_amount
where product_id=products.product_id
and warehouse_id IN('32', '33', '34')
and amount>0 limit 1
) = 1
GROUP BY products.product_id
ORDER BY popularity.total desc, products.product_id ASC
LIMIT 0, 6
Запрос работает моментально (в 10 раз быстрее), если сделать что-то из этого:
- Убрать GROUP BY products.product_id ИЛИ
- Убрать ORDER BY popularity.total desc ИЛИ
- Убрать подзапрос в условии
К сожалению, explain в mysql не дал мне понять, что происходит иначе когда меняешь запрос.
Я попробовал вместо подзапроса о наличии товара сделать left join - стало еще хуже.
Group by - если убрать, то будут дублирующие товары, т.к. в prices несколько строк соответствуют одному товару. + Мне нужно получить минимальную стоимость этого товара.
Возможно, есть способы избавится от group by, но тогда в других подобных запросах придётся также искать подход для избавления от group by.
Мне интересно, почему этот подзапрос плохо работает вместе с group by / order by popularity . Может быть есть способы как-то сделать иначе ? Или же единственное, что можно сделать это избавится от group by? Если да, то как в такой ситуации лучше от него избавится?
UPD. SQL структура таблиц.
CREATE TABLE `warehouses_products_amount` (
`warehouse_id` mediumint(8) UNSIGNED NOT NULL,
`product_id` mediumint(8) UNSIGNED NOT NULL,
`amount` decimal(8,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `warehouses_products_amount`
ADD PRIMARY KEY (`product_id`,`warehouse_id`),
ADD KEY `product_id` (`product_id`);
CREATE TABLE `products` (
`product_id` mediumint(8) UNSIGNED NOT NULL,
`product_code` varchar(64) NOT NULL DEFAULT '',
`product_type` char(1) NOT NULL DEFAULT 'P',
`status` char(1) NOT NULL DEFAULT 'A',
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `products`
ADD PRIMARY KEY (`product_id`),
ADD KEY `status` (`status`);
ALTER TABLE `products`
MODIFY `product_id` mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT;
CREATE TABLE `product_popularity` (
`product_id` mediumint(8) UNSIGNED NOT NULL DEFAULT 0,
`viewed` int(11) NOT NULL DEFAULT 0,
`added` int(11) NOT NULL DEFAULT 0,
`deleted` int(11) NOT NULL DEFAULT 0,
`bought` int(11) NOT NULL DEFAULT 0,
`total` int(11) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `product_popularity`
ADD PRIMARY KEY (`product_id`),
ADD KEY `total` (`product_id`,`total`);
CREATE TABLE `product_prices` (
`product_id` mediumint(8) UNSIGNED NOT NULL DEFAULT 0,
`price` decimal(12,2) NOT NULL DEFAULT 0.00,
`percentage_discount` int(2) UNSIGNED NOT NULL DEFAULT 0,
`lower_limit` mediumint(8) UNSIGNED NOT NULL DEFAULT 0,
`usergroup_id` mediumint(8) UNSIGNED NOT NULL DEFAULT 0,
`organization_id` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `product_prices`
ADD UNIQUE KEY `usergroup` (`product_id`,`usergroup_id`,`lower_limit`,`organization_id`) USING BTREE,
ADD KEY `product_id` (`product_id`),
ADD KEY `lower_limit` (`lower_limit`),
ADD KEY `usergroup_id` (`usergroup_id`,`product_id`),
ADD KEY `organization_id` (`organization_id`);
