为啥 MySQL 会挂在这个简单的子查询上?

Posted

技术标签:

【中文标题】为啥 MySQL 会挂在这个简单的子查询上?【英文标题】:Why is MySQL hanging on this simple subquery?为什么 MySQL 会挂在这个简单的子查询上? 【发布时间】:2017-07-23 01:25:05 【问题描述】:

我有这个在大约 5 秒内运行并返回大约 500 条记录的简单查询,但是为什么我尝试在复合语句中使用它 mysql 只是挂起

SELECT DISTINCT ARTIST_ID FROM WORK
    GROUP BY ARTIST_ID
    HAVING AVG(WORK_MILLIS_VIEWED) > 10

然而,下面的查询永远不会终止。根据进程列表,它只是创建了一个临时表,尽管它的运行时间应该只比子查询稍长一些,因为艺术家表非常小。

SELECT ARTIST_NAME FROM ARTIST
    WHERE ARTIST_ID IN (SELECT DISTINCT ARTIST_ID
         FROM WORK GROUP BY ARTIST_ID
         HAVING AVG(WORK_MILLIS_VIEWED) > 10)

我犯了一个愚蠢的错误吗?数据库似乎没有做任何其他事情。

【问题讨论】:

【参考方案1】:

MySQL 很难处理 WHERE 子句中的子查询。它通常会决定多次运行子查询(每次与子查询比较的不同 ARTIST_ID 值一次),即使您知道并且我知道子查询不会更改。

解决方法是在 FROM 子句中运行子查询并加入它:

SELECT A.ARTIST_NAME 
FROM (
    SELECT ARTIST_ID FROM WORK 
    GROUP BY ARTIST_ID HAVING AVG(WORK_MILLIS_VIEWED) > 10
) AS T
JOIN ARTIST A ON A.ARTIST_ID = T.ARTIST_ID

这将至少只运行一次子查询,并在连接到另一个表实例时将其存储在一个临时表中。

您还将受益于按该顺序对列对 (ARTIST_ID, WORK_MILLIS_VIEWED) 的索引。

【讨论】:

啊,我明白了,所以 MySQL 正在重复聚合查询,这大约需要 500x5 秒 - 但我可以更改隔离级别,它应该会消失。 @awiebe - 如果隔离级别有这种效果,请告诉我们。 @Rick James - 好吧,看起来我可以更改隔离级别,因为我的数据库不活跃,这适用于我的应用程序,但对其他人不利。在这种情况下,最好的办法似乎是获取子查询表上的锁。 好的,最后,我使用了表锁,将统计数据拉入我的业务逻辑,然后将它们放回艺术家中并进行更新。此操作不适用于事务存储引擎,因此您能做的最好的事情就是暂停事务片刻,快速运行批处理作业,更新然后解锁。

以上是关于为啥 MySQL 会挂在这个简单的子查询上?的主要内容,如果未能解决你的问题,请参考以下文章

生产库中遇到mysql的子查询

应该是独立的子查询不是。为啥?

为啥当我的子查询无效时,此 SQL 查询会起作用? - 甲骨文 [重复]

MySQL:子查询检查超过 14000 行的子查询优化问题

为啥在本机查询 Hibernate 延迟加载的子实体中?

基于 Swing 的加载框架挂在 DB 查询上