MySQL 8:使用 GROUP BY 的子查询和使用 GROUP BY 的 INNER JOIN 之间的查询性能

问题描述 投票:0回答:1

我必须通过一个或多个类别 ID 检索帖子列表。 我不想在我的结果中有重复的帖子。

我只对与 MySQL 8 直接相关或可以建立关系的响应感兴趣

我正在考虑两个查询,我正在决定哪个更好。或者,如果有更好的“第三查询”,请指教。

考虑一个简单的两表结构:

CREATE TABLE `job_category_posting` (
  `category_posting_id` int UNSIGNED NOT NULL,
  `category_posting_category_id` int UNSIGNED NOT NULL,
  `category_posting_posting_id` int UNSIGNED NOT NULL,
  `category_posting_is_primary_category` tinyint UNSIGNED DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `job_posting` (
  `posting_id` int UNSIGNED NOT NULL,
  `posting_title` varchar(250) NOT NULL,
  `posting_body` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

ALTER TABLE `job_category_posting`
  ADD PRIMARY KEY (`category_posting_id`),
  ADD UNIQUE KEY `category_posting_category_id` (`category_posting_category_id`,`category_posting_posting_id`),
  ADD UNIQUE KEY `category_posting_is_primary_category` (`category_posting_is_primary_category`,`category_posting_posting_id`),
  ADD KEY `category_posting_posting_id` (`category_posting_posting_id`) USING BTREE;

ALTER TABLE `job_posting`
  ADD PRIMARY KEY (`posting_id`),
  ADD UNIQUE KEY `posting_reserve_id` (`posting_reserve_id`),
  ADD KEY `posting_title` (`posting_title`);

第一个查询(带有 GROUP BY 的子查询):

SELECT t1.*
FROM job_posting AS t1
WHERE (t1.posting_id) IN(
   SELECT category_posting_posting_id
   FROM job_category_posting
   WHERE category_posting_category_id IN (2,13,22,23,24,25)
   GROUP BY category_posting_posting_id
)

快速肮脏的速度测试(不告诉我太多):

  • 0.0017 秒
  • 0.0016 秒
  • 0.0011 秒
  • 0.0017 秒

EXPLAIN 给我这个:

我注意到的:

  • 查询计划经过相当多的行(2356 + 1 + 1935)得到结果
  • 没有临时表。仅使用索引。

第二个查询(INNER JOIN with GROUP BY):

 SELECT job_posting.*
 FROM job_category_posting
 inner join job_posting on job_category_posting.category_posting_posting_id = job_posting.posting_id
 WHERE category_posting_category_id IN (2,13,22,23,24,25)
GROUP BY category_posting_posting_id

快速肮脏的速度测试(不告诉我太多):

  • 0.0016 秒
  • 0.0011 秒
  • 0.0010 秒
  • 0.0019 秒

EXPLAIN 给我这个:

我注意到的:

  • 查询计划只经过了 1935 + 1 行
  • 但是它使用了临时表

所以我的问题是,哪个更好?有没有更好的解释可以证明它的合理性?我只需要一些确凿的事实和证据。

或者我应该尝试第三个查询吗?

任何建议表示赞赏!

mysql subquery query-optimization mysql-8.0
1个回答
0
投票

几件事:

  1. 你的两个查询都有合适的索引。

  2. 执行计划经常随着表的增长而改变。您为在小表上节省几百微秒所做的工作对大表不一定有用。随着表的增长,您可能需要重新审视执行计划。

  3. “使用临时表”并不意味着您的查询使用了成熟的磁盘临时表。它只是意味着软件在对您的情况进行重复数据删除之前将结果集累积到临时数据结构中。 (只有当临时数据结构对于 RAM 来说太大时,软件才会使用磁盘结构。你的肯定适合 RAM。)不要被执行计划中不准确但历史悠久的语言所愚弄

    extra
    柱子。 “使用临时”是可以的。

  4. value IN (set of values)
    谓词自动对值集进行重复数据删除。因此,您的第一个查询可以在没有 GROUP BY 的情况下重写。

    SELECT t1.*
      FROM job_posting AS t1
     WHERE t1.posting_id IN (
            SELECT category_posting_posting_id
              FROM job_category_posting
             WHERE category_posting_category_id IN (2,13,22,23,24,25)
           )
    

    这是我会使用的查询,因为(在我看来)它最清楚地表达了你的意图。而且,我怀疑它可以更好地扩展到大型表,因为它只对一组

    posting_id
    值而不是整行进行重复数据删除工作。

  5. 您的第二个查询滥用了 MySQL 臭名昭著的非标准扩展 GROUP BY。使用

    SET sql_mode = CONCAT_WS(',',@@sql_mode, 'ONLY_FULL_GROUP_BY')
    禁用该扩展,然后再次尝试您的查询。您的 GROUP BY 子句中需要更多条款。更好的是,摆脱 GROUP BY 并使用 DISTINCT,就像这样。

    SELECT DISTINCT job_posting.*
      FROM job_category_posting
     inner join job_posting
         on job_category_posting.category_posting_posting_id = job_posting.posting_id
     WHERE category_posting_category_id IN (2,13,22,23,24,25)
    

    但这必须对整行进行重复数据删除。

© www.soinside.com 2019 - 2024. All rights reserved.