sql >> Database >  >> RDS >> Mysql

Optimaliseer een query die resultaten groepeert op een veld uit de samengevoegde tabel

Het probleem is dat groeperen op name zorgt ervoor dat u de sales_id . verliest informatie, daarom is MySQL gedwongen een tijdelijke tabel te gebruiken.

Hoewel het niet de schoonste van de oplossingen is, en een van mijn minder favoriete benaderingen, zou je een nieuwe index kunnen toevoegen, op beide de name en de sales_id kolommen, zoals:

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

en dwingen de vraag om deze index te gebruiken, met ofwel force index of 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;

Mijn uitvoering rapporteerde alleen "waarbij gebruiken; index gebruiken" op de tabel p en "waar gebruiken" op de tafel s.

Hoe dan ook, ik raad je ten zeerste aan om opnieuw na te denken over je schema, omdat je waarschijnlijk een beter ontwerp voor deze twee tabellen zult vinden. Aan de andere kant, als dit geen cruciaal onderdeel van uw toepassing is, kunt u de "geforceerde" index aanpakken.

BEWERKEN

Aangezien het vrij duidelijk is dat het probleem in het ontwerp zit, raad ik aan om de relaties als een veel-op-veel te tekenen. Als je de kans hebt om het te verifiëren in je testomgeving, zou ik het volgende doen:

1) Maak een tijdelijke tabel om de naam en id van het product op te slaan:

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

2) Begin bij de tijdelijke tabel en voeg u bij de verkooptabel om een ​​vervanging te maken voor de 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) Maak de join-tabel:

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

en vul het met de bestaande waarden:

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;

Tot slot de vraag om mee te doen:

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;

Houd er rekening mee dat de groep op de primaire sleutel staat, niet de naam.

Uitvoer uitleggen:

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. Hoe te ontsnappen aan speciale SQL-tekens in reguliere expressies in Mysql

  2. ALTER TABLE-fout

  3. Verbinding maken met MySQL Server op localhost via Docker

  4. Verschil tussen inline en out-of-line beperkingen