Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Optimieren Sie eine Abfrage, die Ergebnisse nach einem Feld aus der verknüpften Tabelle gruppiert

Das Problem ist die Gruppierung nach name lässt Sie die sales_id verlieren Informationen, daher ist MySQL gezwungen, eine temporäre Tabelle zu verwenden.

Obwohl es nicht die sauberste der Lösungen ist und einer meiner weniger bevorzugten Ansätze, könnten Sie beiden einen neuen Index hinzufügen der name und die sales_id Spalten, wie:

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

und zwingen die Abfrage, um diesen Index zu verwenden, entweder mit force index oder 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;

Meine Ausführung meldete nur "using where; using index" für die Tabelle p und "using where" für die Tabelle s.

Wie auch immer, ich empfehle Ihnen dringend, Ihr Schema zu überdenken, da Sie wahrscheinlich ein besseres Design für diese beiden Tabellen finden. Wenn dies jedoch kein kritischer Teil Ihrer Anwendung ist, können Sie sich mit dem "erzwungenen" Index befassen.

BEARBEITEN

Da es ziemlich klar ist, dass das Problem im Design liegt, schlage ich vor, die Beziehungen als Viele-zu-Viele zu zeichnen. Wenn Sie die Möglichkeit haben, es in Ihrer Testumgebung zu verifizieren, würde ich Folgendes tun:

1) Erstellen Sie eine temporäre Tabelle, um nur den Namen und die ID des Produkts zu speichern:

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

2) Beginnen Sie mit der temporären Tabelle und treten Sie der Verkaufstabelle bei, um einen Ersatz für ycs_product zu erstellen :

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) Erstellen Sie die Join-Tabelle:

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

und füllen Sie es mit den vorhandenen Werten:

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;

Zum Schluss die Join-Abfrage:

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;

Bitte beachten Sie, dass die Gruppierung auf dem Primärschlüssel steht, nicht auf dem Namen.

Ausgabe erklären:

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