sql >> Databasteknik >  >> RDS >> Mysql

Optimera en fråga som grupperar resultat efter ett fält från den sammanfogade tabellen

Problemet är att gruppera efter name gör att du tappar sales_id information, därför tvingas MySQL använda en tillfällig tabell.

Även om det inte är den renaste av lösningarna och en av mina mindre favoriter, kan du lägga till ett nytt index på båda name och sales_id kolumner, som:

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

och tvinga frågan för att använda detta index, med antingen force index eller 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;

Min körning rapporterade bara "använder var; använder index" på tabellen p och "använder var" på tabellen s.

Hur som helst, jag rekommenderar starkt att du tänker om på ditt schema, eftersom du förmodligen kan hitta en bättre design för dessa två tabeller. Å andra sidan, om detta inte är en kritisk del av din ansökan, kan du hantera det "tvingade" indexet.

REDIGERA

Eftersom det är ganska tydligt att problemet ligger i designen föreslår jag att man ritar relationerna som många-till-många. Om du har möjlighet att verifiera det i din testmiljö, så här skulle jag göra:

1) Skapa en tillfällig tabell bara för att lagra namn och id för produkten:

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

2) Starta från den tillfälliga tabellen, gå med i försäljningstabellen för att skapa en ersättning för 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) Skapa kopplingstabellen:

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

och fyll den med befintliga värden:

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;

Slutligen, anslutningsfrågan:

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;

Observera att gruppen efter finns på primärnyckeln, inte namnet.

Förklara utdata:

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. Rails 4 LIKE-fråga - ActiveRecord lägger till citat

  2. Microsoft Access memo datatyp till MySQL Datatype

  3. LAST_INSERT_ID() MySQL

  4. Få en lista över datum mellan två datum