我如何优化具有多对多关系的 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;
这应该比您的版本快得多。 . .但是,您的版本非常快,所以您可能不会注意到很大的不同。
这是我执行的查询:
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;
这应该比您的版本快得多。 . .但是,您的版本非常快,所以您可能不会注意到很大的不同。