Problem polega na tym, że grupowanie według name
sprawia, że tracisz sales_id
informacji, dlatego MySQL jest zmuszony do korzystania z tabeli tymczasowej.
Chociaż nie jest to najczystsze z rozwiązań i jedno z moich mniej ulubionych rozwiązań, możesz dodać nowy indeks w obu name
i sales_id
kolumny, takie jak:
ALTER TABLE `yourdb`.`ycs_products`
ADD INDEX `name_sales_id_idx` (`name` ASC, `sales_id` ASC);
i wymuszanie zapytanie, aby użyć tego indeksu, z force index
lub 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;
Moje wykonanie zgłosiło tylko „przy użyciu gdzie; przy użyciu indeksu” w tabeli p i „przy użyciu gdzie” w tabeli s.
W każdym razie zdecydowanie sugeruję ponowne przemyślenie swojego schematu, ponieważ prawdopodobnie znajdziesz lepszy projekt dla tych dwóch tabel. Z drugiej strony, jeśli nie jest to krytyczna część Twojej aplikacji, możesz poradzić sobie z „wymuszonym” indeksem.
EDYTUJ
Ponieważ jest całkiem jasne, że problem tkwi w projekcie, sugeruję rysowanie relacji jako wiele do wielu. Jeśli masz szansę zweryfikować to w swoim środowisku testowym, oto co zrobię:
1) Utwórz tabelę tymczasową tylko do przechowywania nazwy i identyfikatora produktu:
create temporary table tmp_prods
select min(id) id, name
from ycs_products
group by name;
2) Zaczynając od tabeli tymczasowej, dołącz do tabeli sprzedaży, aby utworzyć zamiennik dla 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) Utwórz tabelę łączenia:
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);
i wypełnij go istniejącymi wartościami:
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;
Na koniec zapytanie o dołączenie:
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;
Pamiętaj, że grupowanie według jest kluczem podstawowym, a nie nazwą.
Wyjaśnij wyjście:
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 |
+------+-------------+-------+--------+---------------------+---------+---------+-----------------+------+-------------+