Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Zoptymalizuj zapytanie, które grupuje wyniki według pola z połączonej tabeli

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 |
+------+-------------+-------+--------+---------------------+---------+---------+-----------------+------+-------------+


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak zaimplementować wydajniejszą funkcję wyszukiwania?

  2. Funkcja rankingowa w MySQL

  3. Erlang Mysql:Jak zapobiegać wstrzykiwaniu SQL

  4. BIGINT UNSIGNED VALUE JEST poza zakresem Mój SQL

  5. Laravel 4 Kaskadowe miękkie usuwanie