【问题标题】:MySQL performance difference between JOIN and INJOIN和IN之间的MySQL性能差异
【发布时间】:2013-08-07 17:58:05
【问题描述】:

我想在约 5m 行的表中查找所有具有后继的每小时记录。

我试过了:

SELECT DISTINCT (date_time)
FROM my_table
JOIN (SELECT DISTINCT (DATE_ADD( date_time, INTERVAL 1 HOUR)) date_offset
      FROM my_table) offset_dates
ON date_time = date_offset

SELECT DISTINCT(date_time)
FROM my_table
WHERE date_time IN (SELECT DISTINCT(DATE_ADD(date_time, INTERVAL 1 HOUR))
                    FROM my_table)

第一个在几秒钟内完成,秒挂几个小时。 我可以理解越早越好,但为什么会有如此巨大的性能差距?

-------- 编辑 ---------------

这是两个查询的EXPLAIN

id  select_type table       type    possible_keys   key     key_len ref                         rows    Extra
1   PRIMARY     <derived2>  ALL     NULL            NULL    NULL    NULL                        1710    Using temporary
1   PRIMARY     my_table    ref     PRIMARY         PRIMARY 8       offset_dates.date_offset    555     Using index
2   DERIVED     my_table    index   NULL            PRIMARY 13      NULL                        5644204 Using index; Using temporary


id  select_type        table    type    possible_keys   key     key_len ref     rows    Extra
1   PRIMARY            my_table range   NULL            PRIMARY 8       NULL    9244    Using where; Using index for group-by
2   DEPENDENT SUBQUERY my_table index   NULL            PRIMARY 13      NULL    5129983 Using where; Using index; Using temporary

【问题讨论】:

  • 我可能错了,但是子查询很重。因此,如果您使用JOINON 子句执行它,它会更快,因为它不会检索子选择的每个结果。在第二个查询中,您首先检索子选择的所有结果,然后查看它。

标签: mysql


【解决方案1】:

一般来说,使用连接的查询比使用IN (...) 的等效查询执行得更好,因为前者可以利用索引而后者不能;必须针对可能返回的每一行扫描整个 IN 列表。

(请注意,在这种情况下,某些数据库引擎的性能要优于其他引擎;例如,SQL Server can produce equivalent performance for both types of queries。)

您可以通过在查询前面加上 EXPLAIN 并运行它来查看 MySQL 查询优化器打算如何处理给定的 SELECT 查询。除其他外,这将为您提供引擎必须为查询中的每个步骤检查的行数;将这些计数相乘以获得引擎必须访问的总行数,这可以作为对可能性能的粗略估计。

【讨论】:

  • 谢谢,我添加了对查询的解释,但我在阅读时遇到了一些麻烦!
【解决方案2】:

我会在两个查询前加上explain,然后比较访问计划的差异。您可能会发现第一个查询查看的行数比第二个少得多。

但我的预感是 JOIN 的应用比 WHERE 子句更直接。因此,在 WHERE 子句中,您从my_table 获取每条记录,应用算术函数,然后对它们进行排序,因为select distinct 通常需要排序,有时它会在内存或磁盘上创建一个临时表。检查的行数可能是每个表大小的乘积。

但是在 JOIN 子句中,很多在 WHERE 子句中被检查和排序的行可能被预先消除了。您最终可能会看到更少的行......并且数据库可能会采取更简单的措施来完成它。

但我认为这篇文章最能回答你的问题:SQL fixed-value IN() vs. INNER JOIN performance

【讨论】:

  • 谢谢,我在我的问题中添加了EXPLAIN。我不熟悉这种查询计划,但显然它们完全不同。
【解决方案3】:

'IN' 子句对于大表来说通常很慢。据我记得,对于您打印出的第二条语句 - 它会简单地遍历 my_table 的所有行(除非您在那里有索引)检查每一行是否匹配 WHERE 子句。通常,IN 被视为一组 OR 子句,其中包含所有集合元素。 这就是为什么,我认为,使用在 JOIN 查询后台创建的临时表会更快。

这里有一些有用的链接:

MySQL Query IN() Clause Slow on Indexed Column

inner join and where in() clause performance?

http://explainextended.com/2009/08/18/passing-parameters-in-mysql-in-list-vs-temporary-table/

【讨论】:

  • 谢谢,所有事情都指向支持JOIN,但我仍然对差异的大小感到困惑。
  • 考虑到 Aaron Miller 对我的一个的回答 - IN 子句根本不使用索引,正如我所说,只是循环遍历表检查每一行以匹配 IN cluase 的值集;而 JOIN 首先构建临时表(并且您正在为此 JOIN 使用索引,对吗?)然后获取行。
  • 这样,遍历 500 万行将花费大量时间(嗯,实际上取决于一些数据库设置和环境)。希望对您有所帮助。
【解决方案4】:

另一件需要考虑的事情是,与 JOIN 相比,使用您的 IN 样式,未来的优化几乎是不可能的。通过连接,您可以添加一个索引,谁知道呢,这取决于数据集,它可能会加快 2、5、10 倍的速度。使用 IN,它将运行该查询。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-04
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    • 2012-05-26
    • 2013-09-02
    相关资源
    最近更新 更多