0

我必须从一个表中选择一个目录列表,并在另外两个表中执行计数:商店和类别。计数器应该显示有多少商店和类别链接到每个目录。我已经设法使用这个 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 的方法?提前致谢。

4

2 回答 2

2

SELECT正如您所提到的,您可以通过将子句中的依赖子查询重构为JOINed 聚合子查询来加快速度。

第一个子查询可以这样写。

                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进行操作,这样即使目录条目未在您的类别或商店表中提及,您仍会看到它们。

于 2021-05-04T11:05:12.207 回答
0

子查询很好,但您可以简化查询:

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 = 2s.company_id = c.company_id. 这似乎是一个相关性条款。
于 2021-05-04T10:48:45.087 回答