sql >> Databáze >  >> RDS >> Mysql

Optimalizujte dotaz, který seskupuje výsledky podle pole ze spojené tabulky

Problém je v seskupování podle name ztratíte sales_id informace, proto je MySQL nuceno používat dočasnou tabulku.

Ačkoli to není nejčistší z řešení a jeden z mých méně oblíbených přístupů, můžete přidat nový index na obě name a sales_id sloupce, jako:

ALTER TABLE `yourdb`.`ycs_products` 
ADD INDEX `name_sales_id_idx` (`name` ASC, `sales_id` ASC);

a vynutit dotaz použít tento index, buď s force index nebo 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 provedení hlásilo pouze "použití kde; pomocí indexu" v tabulce p a "použití kde" v tabulce s.

Každopádně vám důrazně doporučuji, abyste přehodnotili své schéma, protože pravděpodobně byste mohli najít lepší design pro tyto dvě tabulky. Na druhou stranu, pokud to není kritická část vaší aplikace, můžete se vypořádat s „vynuceným“ indexem.

UPRAVIT

Protože je zcela jasné, že problém je v návrhu, navrhuji nakreslit vztahy jako mnoho k mnoha. Pokud máte možnost to ověřit ve svém testovacím prostředí, udělal bych toto:

1) Vytvořte dočasnou tabulku pouze pro uložení názvu a ID produktu:

create temporary table tmp_prods
select min(id) id, name
from ycs_products
group by name;

2) Počínaje dočasnou tabulkou se připojte k prodejní tabulce a vytvořte náhradu za 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) Vytvořte spojovací tabulku:

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);

a vyplňte jej stávajícími hodnotami:

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;

Nakonec spojovací dotaz:

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;

Vezměte prosím na vědomí, že skupina podle je na primárním klíči, nikoli na jméně.

Vysvětlete výstup:

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. PostgreSQL index na JSON

  2. MySQL a kontrolní omezení

  3. Problém s kódováním znaků

  4. Anotace Spring Data @CreatedDate mi nefunguje