【问题标题】:MySQL WHERE NOT IN extremely slowMySQL WHERE 不是非常慢
【发布时间】:2012-12-19 12:29:12
【问题描述】:

以下是存储过程中的 SQL 语句(为了简洁而截断):

SELECT * 
FROM item a 
WHERE a.orderId NOT IN (SELECT orderId FROM table_excluded_item);

这个语句需要 30 秒左右!但是如果我删除内部 SELECT 查询,它会下降到 1s。 table_excluded_item 并不大,但我怀疑内部查询的执行次数超出了它的需要。

有没有更有效的方法?

【问题讨论】:

  • 内部查询是一个依赖子查询,这是众所周知的瓶颈,因为子查询是针对外部查询的每一行运行的。在 MySQL 开发网站上查看 Optimizing Subqueries

标签: mysql sql query-optimization


【解决方案1】:

使用LEFT JOIN

SELECT  a.* 
FROM    item a 
        LEFT JOIN table_excluded_item b
            ON a.orderId = b.orderId
WHERE   b.orderId IS NULL

确保两个表中的orderId 都已编入索引。

【讨论】:

  • 嘿,这行得通,谢谢!现在下降到2s。这种语言对我来说似乎总是很违反直觉:(
【解决方案2】:

左连接方法的问题是在生成输出时可能会处理重复记录。有时,情况并非如此。 . .根据这个article,当列被索引时,MySQL 会正确优化left outer join,即使存在重复也是如此。不过,我承认对这种优化总是会发生持怀疑态度。

MySQL 有时会在使用子查询优化 IN 语句时遇到问题。最好的解决方法是关联子查询:

SELECT * 
FROM item a 
WHERE not exists (select 1
                  from table_excluded_item tei
                  where tei.orderid = a.orderid
                  limit 1
                 )

如果您在 table_excluded_item.orderid 上有一个索引,那么这将扫描索引并在第一个值处停止(limit 1 可能不是绝对必要的)。这是在 MySQL 中实现您想要的最快和最安全的方法。

【讨论】:

  • 技术上limit 1 是不必要的; “反加入”无论如何都会做同样的事情。 (可能是 mysql 不够聪明,不知道这一点)
  • "左连接方法的问题是你可以在输出中得到重复的记录。" - 为什么?请记住,您正在搜索不存在的记录。
  • @jW 。 . .我改写了这个。
【解决方案3】:

试试这个并与LEFT JOIN查询时间比较:

SELECT * 
FROM item a 
HAVING orderId NOT IN (SELECT orderId FROM table_excluded_item);

这是不赞成的(在可以使用WHERE 时使用HAVING),因为HAVING 假定限制条件(orderId)是结果集的一部分。但我认为在这种情况下它更有意义(因为它是结果集的一部分)并且因为它比LEFT JOIN 方法更清楚正在发生的事情。

实际上可能会慢一些,但请发布结果,以便我们知道它是否比您的原始查询更好。

【讨论】:

  • 干杯 - 在我的数据集上,我始终得到 ~7s 与 ~8s 的左连接方法
猜你喜欢
  • 1970-01-01
  • 2014-07-16
  • 2013-04-10
  • 2011-09-02
  • 2021-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多