优化 SQL 计数
optimizing SQL counts
我必须 select 来自一个 table 的目录列表,并在另外两个 table 中执行计数:商店和类别。计数器应显示有多少商店和类别链接到每个目录。
我已经设法使用此 SQL 查询获得我需要的功能:
SELECT `catalog`.`id` AS `id`,
`catalog`.`name` AS `name`,
(
SELECT COUNT(*)
FROM `category`
WHERE `category`.`catalog_id` = `catalog`.`id`
AND `category`.`is_archive` = 0
AND `category`.`company_id` = 2
) AS `category_count`,
(
SELECT COUNT(*)
FROM `store`
WHERE `store`.`catalog_id` = `catalog`.`id`
AND `store`.`is_archive` = 0
AND `store`.`company_id` = 2
) AS `store_count`
FROM `catalog`
WHERE `catalog`.`company_id` = 2
AND `catalog`.`is_archive` = 0
ORDER BY `catalog`.`id` ASC;
这按预期工作。但我不喜欢执行子查询,因为它们很慢,而且这个查询在大列表上可能执行不佳。有没有使用 JOIN 优化此 SQL 的方法?
提前致谢。
子查询很好,但您可以简化查询:
SELECT c.id, c.name,
COUNT(*) OVER (PARTITION BY c.catalog_id) as category_count,
(SELECT COUNT(*)
FROM store s
WHERE s.catalog_id = s.id AND
s.is_archive = 0 AND
s.company_id = c.company_id
) AS store_count
FROM catalog c
WHERE c.company_id = 2 AND c.is_archive = 0
ORDER BY c.id ASC;
为了提高性能,您需要索引:
catalog(company_id, is_archive, id)
store(catalog_id, company_id, is_archive)
由于外部查询中的过滤,相关子查询可能是从 store
.
获取结果的最佳性能方式
另请注意对查询的一些更改:
- 我去掉了反引号。它们是不必要的,只会使查询混乱。
- 像
c.id as id
这样的表达式是多余的。表达式被赋予 id
作为别名。
- 我将
s.company_id = 2
更改为 s.company_id = c.company_id
。好像是关联子句。
您可以通过将 SELECT
子句中的依赖子查询重构为您提到的 JOIN
ed 聚合子查询来加快速度。
你可以这样写的第一个子查询。
SELECT COUNT(*) num, catalog_id, company_id
FROM category
WHERE is_archive = 0
GROUP BY catalog_id, company_id
第二个像这样
SELECT COUNT(*) num, catalog_id, company_id
FROM store
WHERE is_archive = 0
GROUP BY catalog_id, company_id
然后,在您的主查询中使用它们,就像它们是包含您想要的计数的表一样。
SELECT catalog.id,
catalog.name,
category.num category_count,
store.num store_count
FROM catalog
LEFT JOIN (
SELECT COUNT(*) num, catalog_id, company_id
FROM category
WHERE is_archive = 0
GROUP BY catalog_id, company_id
) category ON catalog.id = category.catalog_id
AND catalog.company_id = category.company_id
LEFT JOIN (
SELECT COUNT(*) num, catalog_id, company_id
FROM store
WHERE is_archive = 0
GROUP BY catalog_id, company_id
) store ON catalog.id = store.catalog_id
AND catalog.company_id = store.company_id
WHERE catalog.is_archive = 0
AND catalog.company_id = 2
ORDER BY catalog.id ASC;
这比您的示例更快,因为每个子查询只需要 运行 一次,而不是每个商品一次。它还具有您只需说 WHERE catalog.company_id = 2
一次的好功能。 MySQL 优化器知道该怎么做。
我建议 LEFT JOIN
操作,这样您仍然会看到目录条目,即使它们没有在您的类别或商店表格中提及。
我必须 select 来自一个 table 的目录列表,并在另外两个 table 中执行计数:商店和类别。计数器应显示有多少商店和类别链接到每个目录。 我已经设法使用此 SQL 查询获得我需要的功能:
SELECT `catalog`.`id` AS `id`,
`catalog`.`name` AS `name`,
(
SELECT COUNT(*)
FROM `category`
WHERE `category`.`catalog_id` = `catalog`.`id`
AND `category`.`is_archive` = 0
AND `category`.`company_id` = 2
) AS `category_count`,
(
SELECT COUNT(*)
FROM `store`
WHERE `store`.`catalog_id` = `catalog`.`id`
AND `store`.`is_archive` = 0
AND `store`.`company_id` = 2
) AS `store_count`
FROM `catalog`
WHERE `catalog`.`company_id` = 2
AND `catalog`.`is_archive` = 0
ORDER BY `catalog`.`id` ASC;
这按预期工作。但我不喜欢执行子查询,因为它们很慢,而且这个查询在大列表上可能执行不佳。有没有使用 JOIN 优化此 SQL 的方法? 提前致谢。
子查询很好,但您可以简化查询:
SELECT c.id, c.name,
COUNT(*) OVER (PARTITION BY c.catalog_id) as category_count,
(SELECT COUNT(*)
FROM store s
WHERE s.catalog_id = s.id AND
s.is_archive = 0 AND
s.company_id = c.company_id
) AS store_count
FROM catalog c
WHERE c.company_id = 2 AND c.is_archive = 0
ORDER BY c.id ASC;
为了提高性能,您需要索引:
catalog(company_id, is_archive, id)
store(catalog_id, company_id, is_archive)
由于外部查询中的过滤,相关子查询可能是从 store
.
另请注意对查询的一些更改:
- 我去掉了反引号。它们是不必要的,只会使查询混乱。
- 像
c.id as id
这样的表达式是多余的。表达式被赋予id
作为别名。 - 我将
s.company_id = 2
更改为s.company_id = c.company_id
。好像是关联子句。
您可以通过将 SELECT
子句中的依赖子查询重构为您提到的 JOIN
ed 聚合子查询来加快速度。
你可以这样写的第一个子查询。
SELECT COUNT(*) num, catalog_id, company_id
FROM category
WHERE is_archive = 0
GROUP BY catalog_id, company_id
第二个像这样
SELECT COUNT(*) num, catalog_id, company_id
FROM store
WHERE is_archive = 0
GROUP BY catalog_id, company_id
然后,在您的主查询中使用它们,就像它们是包含您想要的计数的表一样。
SELECT catalog.id,
catalog.name,
category.num category_count,
store.num store_count
FROM catalog
LEFT JOIN (
SELECT COUNT(*) num, catalog_id, company_id
FROM category
WHERE is_archive = 0
GROUP BY catalog_id, company_id
) category ON catalog.id = category.catalog_id
AND catalog.company_id = category.company_id
LEFT JOIN (
SELECT COUNT(*) num, catalog_id, company_id
FROM store
WHERE is_archive = 0
GROUP BY catalog_id, company_id
) store ON catalog.id = store.catalog_id
AND catalog.company_id = store.company_id
WHERE catalog.is_archive = 0
AND catalog.company_id = 2
ORDER BY catalog.id ASC;
这比您的示例更快,因为每个子查询只需要 运行 一次,而不是每个商品一次。它还具有您只需说 WHERE catalog.company_id = 2
一次的好功能。 MySQL 优化器知道该怎么做。
我建议 LEFT JOIN
操作,这样您仍然会看到目录条目,即使它们没有在您的类别或商店表格中提及。