Проблемът е, че групирането по name
ви кара да загубите sales_id
информация, следователно MySQL е принуден да използва временна таблица.
Въпреки че не е най-чистото от решенията и един от по-малко любимите ми подходи, можете да добавите нов индекс и за и двете name
и sales_id
колони, като:
ALTER TABLE `yourdb`.`ycs_products`
ADD INDEX `name_sales_id_idx` (`name` ASC, `sales_id` ASC);
исилата заявката за използване на този индекс с force index
или use index
:
SELECT SQL_NO_CACHE p.name, COUNT(1) FROM ycs_sales s
INNER JOIN ycs_products p use index(name_sales_id_idx) ON s.id = p.sales_id
WHERE s.dtm BETWEEN '2018-02-16 00:00:00' AND '2018-02-22 23:59:59'
GROUP BY p.name;
Моето изпълнение отчете само „използване на where; използване на индекс“ в таблица p и „използване на where“ в таблица s.
Както и да е, силно ви предлагам да преосмислите схемата си, защото вероятно ще намерите по-добър дизайн за тези две таблици. От друга страна, ако това не е критична част от приложението ви, можете да се справите с „принудителния“ индекс.
РЕДАКТИРАНЕ
Тъй като е съвсем ясно, че проблемът е в дизайна, предлагам да нарисувате връзките като много към много. Ако имате възможност да го потвърдите във вашата среда за тестване, ето какво бих направил:
1) Създайте временна таблица само за съхраняване на името и идентификатора на продукта:
create temporary table tmp_prods
select min(id) id, name
from ycs_products
group by name;
2) Започвайки от временната таблица, присъединете се към таблицата за продажби, за да създадете заместител на ycs_product
:
create table ycs_products_new
select * from tmp_prods;
ALTER TABLE `poc`.`ycs_products_new`
CHANGE COLUMN `id` `id` INT(11) NOT NULL ,
ADD PRIMARY KEY (`id`);
3) Създайте таблицата за присъединяване:
CREATE TABLE `prod_sale` (
`prod_id` INT(11) NOT NULL,
`sale_id` INT(11) NOT NULL,
PRIMARY KEY (`prod_id`, `sale_id`),
INDEX `sale_fk_idx` (`sale_id` ASC),
CONSTRAINT `prod_fk`
FOREIGN KEY (`prod_id`)
REFERENCES ycs_products_new (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `sale_fk`
FOREIGN KEY (`sale_id`)
REFERENCES ycs_sales (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
и го попълнете със съществуващите стойности:
insert into prod_sale (prod_id, sale_id)
select tmp_prods.id, sales_id from ycs_sales s
inner join ycs_products p
on p.sales_id=s.id
inner join tmp_prods on tmp_prods.name=p.name;
И накрая, заявката за присъединяване:
select name, count(name) from ycs_products_new p
inner join prod_sale ps on ps.prod_id=p.id
inner join ycs_sales s on s.id=ps.sale_id
WHERE s.dtm BETWEEN '2018-02-16 00:00:00' AND '2018-02-22 23:59:59'
group by p.id;
Моля, имайте предвид, че групата от е на първичния ключ, а не в името.
Обяснете изхода:
explain select name, count(name) from ycs_products_new p inner join prod_sale ps on ps.prod_id=p.id inner join ycs_sales s on s.id=ps.sale_id WHERE s.dtm BETWEEN '2018-02-16 00:00:00' AND '2018-02-22 23:59:59' group by p.id;
+------+-------------+-------+--------+---------------------+---------+---------+-----------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+---------------------+---------+---------+-----------------+------+-------------+
| 1 | SIMPLE | p | index | PRIMARY | PRIMARY | 4 | NULL | 3 | |
| 1 | SIMPLE | ps | ref | PRIMARY,sale_fk_idx | PRIMARY | 4 | test.p.id | 1 | Using index |
| 1 | SIMPLE | s | eq_ref | PRIMARY,dtm | PRIMARY | 4 | test.ps.sale_id | 1 | Using where |
+------+-------------+-------+--------+---------------------+---------+---------+-----------------+------+-------------+