简体   繁体   English

如何优化具有多对多关系的 mysql COUNT() 查询(通过第 3 个表)

[英]How can i optimize the mysql COUNT() query with many-to-many relationship(through the 3rd table)

Here is the query that i execute:这是我执行的查询:

SELECT COUNT(*)
FROM (SELECT `p`.*
      FROM `shop_products` `p` LEFT JOIN
           `shop_tag_assignments`
           ON `p`.`id` = `shop_tag_assignments`.`product_id` LEFT JOIN
           `shop_tags`
           ON `shop_tag_assignments`.`tag_id` = `shop_tags`.`id`
      WHERE `p`.`status`=1
      GROUP BY `p`.`id`
     ) `c`

This query takes about 300 milliseconds(i think it's too long..)这个查询需要大约 300 毫秒(我认为它太长了..)

EXPLAIN QUERY:解释查询:

EXPLAIN QUERY IMAGE解释查询图像

DB tables dump:数据库表转储:

1k records in shop_tags table shop_tags表中有 1k 条记录

CREATE TABLE `shop_tags` (
  `id` int(11) NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `label` text COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

ALTER TABLE `shop_tags`
  ADD PRIMARY KEY (`id`),
  ADD UNIQUE KEY `idx-shop_tags-slug` (`slug`),
  ADD KEY `id` (`id`);

ALTER TABLE `shop_tags`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1162;
COMMIT;

Table structure for shop_tag_assignments : shop_tag_assignments 的表结构:

224k records in shop_tag_assignments table shop_tag_assignments表中有 224k 条记录

CREATE TABLE `shop_tag_assignments` (
  `product_id` int(11) NOT NULL,
  `tag_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `shop_tag_assignments`
  ADD PRIMARY KEY (`product_id`,`tag_id`),
  ADD KEY `idx-shop_tag_assignments-product_id` (`product_id`),
  ADD KEY `idx-shop_tag_assignments-tag_id` (`tag_id`),
  ADD KEY `_index_name` (`product_id`,`tag_id`),
  ADD KEY `__index_name` (`tag_id`,`product_id`);

ALTER TABLE `shop_tag_assignments`
  ADD CONSTRAINT `fk-shop_tag_assignments-product_id` FOREIGN KEY (`product_id`) REFERENCES `shop_products` (`id`) ON DELETE CASCADE,
  ADD CONSTRAINT `fk-shop_tag_assignments-tag_id` FOREIGN KEY (`tag_id`) REFERENCES `shop_tags` (`id`) ON DELETE CASCADE;
COMMIT;

Mysql version : 5.7.16-10-log Mysql 版本:5.7.16-10-log

Get rid of the subquery.摆脱子查询。 And don't use backticks so much:并且不要过多使用反引号:

SELECT COUNT(DISTINCT p.id)
FROM shop_products p LEFT JOIN
     shop_tag_assignments sta
     ON p.id = sta.product_id LEFT JOIN
     shop_tags st
     ON sta.tag_id = st.id
WHERE p.status = 1;

For this query, you want indexes on shop_products(status, id) .对于此查询,您需要shop_products(status, id)上的索引。

Next, your query is counting the number of rows returned by the inner subquery after aggregation by p.id .接下来,您的查询正在计算p.id聚合后内部子查询返回的行数。 You are using LEFT JOIN , so nothing is being filtered out.您正在使用LEFT JOIN ,因此没有过滤掉任何内容。 That means that the joins are really redundant.这意味着连接真的是多余的。 I think this does the same thing:我认为这做同样的事情:

SELECT COUNT(*)
FROM shop_products p 
WHERE p.status = 1;

This should be much faster than your version .这应该比你的版本快得多。 . . . . however, your version is pretty fast so you might not notice a really big difference.但是,您的版本非常快,因此您可能不会注意到很大的差异。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM