我必须从一个表中选择一个目录列表,并在另外两个表中执行计数:Stores 和 Categories。计数器应显示有多少商店和类别链接到每个目录。 我设法使用此 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;
这按预期工作。但我不喜欢执行子查询,因为它们很慢,而且这个查询在 LARGE 列表上可能执行得很差。有没有什么方法可以使用 JOIN 优化这个 SQL? 提前致谢。
答案 0 :(得分:2)
您可以通过将 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
操作,这样即使您的类别或商店表中未提及目录条目,您仍会看到它们。
答案 1 :(得分:0)
子查询很好,但您可以简化查询:
function toInt($str)
{
return (int)$str;
}
为了性能,您需要索引:
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
。这似乎是一个相关条款。