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

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

这是我执行的查询:

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`

这个查询大约需要 300 毫秒(我觉得太长了..)

解释查询:

EXPLAIN QUERY IMAGE

DB tables 转储:

1k 条记录在 shop_tags table

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 结构 shop_tag_assignments:

shop_tag_assignmentstable

中的 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版本: 5.7.16-10-日志

去掉子查询。并且不要过多使用反引号:

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;

对于此查询,您需要 shop_products(status, id).

上的索引

接下来,您的查询将计算 p.id 聚合后内部子查询返回的行数。您正在使用 LEFT JOIN,因此没有过滤掉任何内容。这意味着连接确实是多余的。我认为这做同样的事情:

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

这应该比您的版本快得多。 . .但是,您的版本非常快,所以您可能不会注意到很大的不同。