MySQL - SELECT WHERE字段IN(子查询) - 为什么这么慢?

qua*_*ano 124 mysql subquery where-in

我在数据库中有两个重复项,我想检查,所以我做了什么看到重复,我这样做:

SELECT relevant_field
FROM some_table
GROUP BY relevant_field
HAVING COUNT(*) > 1
Run Code Online (Sandbox Code Playgroud)

这样,我将获得所有具有related_field的行不止一次.此查询需要几毫秒才能执行.

现在,我想检查每个重复项,所以我想我可以在上面的查询中使用related_field选择some_table中的每一行,所以我喜欢这样:

SELECT *
FROM some_table 
WHERE relevant_field IN
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
)
Run Code Online (Sandbox Code Playgroud)

由于某种原因(这需要几分钟),结果显然是极慢的.究竟是什么让它变慢?related_field已编入索引.

最后我尝试从第一个查询创建一个视图"temp_view" (SELECT relevant_field FROM some_table GROUP BY relevant_field HAVING COUNT(*) > 1),然后再这样做我的第二个查询:

SELECT *
FROM some_table
WHERE relevant_field IN
(
    SELECT relevant_field
    FROM temp_view
)
Run Code Online (Sandbox Code Playgroud)

这很好用.MySQL在几毫秒内完成此任务.

这里有任何SQL专家可以解释发生了什么吗?

Joh*_*ica 110

将查询重写为此

SELECT st1.*, st2.relevant_field FROM sometable st1
INNER JOIN sometable st2 ON (st1.relevant_field = st2.relevant_field)
GROUP BY st1.id  /* list a unique sometable field here*/
HAVING COUNT(*) > 1
Run Code Online (Sandbox Code Playgroud)

我认为st2.relevant_field必须在select中,因为否则该having子句会给出错误,但我不是100%肯定

切勿使用IN子查询; 这是出了名的慢.
只能使用IN固定的值列表.

更多提示

  1. 如果您想更快地进行查询,请不要SELECT *只选择您真正需要的字段.
  2. 确保你有一个索引relevant_field来加速equi-join.
  3. 确保group by在主键上.
  4. 如果您使用InnoDB 并且只选择索引字段(并且事情并不复杂),那么MySQL将仅使用索引来解析您的查询,从而加快速度.

90%的IN (select 查询的一般解决方案

使用此代码

SELECT * FROM sometable a WHERE EXISTS (
  SELECT 1 FROM sometable b
  WHERE a.relevant_field = b.relevant_field
  GROUP BY b.relevant_field
  HAVING count(*) > 1) 
Run Code Online (Sandbox Code Playgroud)


qua*_*ano 102

正在为每一行运行子查询,因为它是一个相关查询.通过从子查询中选择所有内容,可以将相关查询转换为非相关查询,如下所示:

SELECT * FROM
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
) AS subquery
Run Code Online (Sandbox Code Playgroud)

最终查询将如下所示:

SELECT *
FROM some_table
WHERE relevant_field IN
(
    SELECT * FROM
    (
        SELECT relevant_field
        FROM some_table
        GROUP BY relevant_field
        HAVING COUNT(*) > 1
    ) AS subquery
)
Run Code Online (Sandbox Code Playgroud)

  • 您能解释一下是什么使它成为相关子查询吗?我的理解是,当子查询使用依赖于外部查询的值时,子查询就会变得相关。但在这个例子中我看不到任何相互依赖关系。对于外部查询返回的每一行,它都会给出相同的结果。我在 MariaDB 上实现了一个类似的示例,并且我没有看到性能受到影响(到目前为止),所以我想清楚地看到何时需要这种“SELECT *”包装。 (7认同)
  • 这对我来说非常好.我在IN(子查询)中有另一个IN(子查询),它花了超过10分钟,所以我在等待的时候用Google搜索了.按照建议将每个子查询包装在SELECT*FROM()中将其减少到2秒! (3认同)

cet*_*ras 7

SELECT st1.*
FROM some_table st1
inner join 
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
)st2 on st2.relevant_field = st1.relevant_field;
Run Code Online (Sandbox Code Playgroud)

我已经在我的一个数据库上尝试过您的查询,还尝试将其重写为子查询的连接。

这工作得更快,试试吧!


pla*_*ang 5

我已经用 www.prettysql.net 重新格式化了你的慢 sql 查询

SELECT *
FROM some_table
WHERE
 relevant_field in
 (
  SELECT relevant_field
  FROM some_table
  GROUP BY relevant_field
  HAVING COUNT ( * ) > 1
 );
Run Code Online (Sandbox Code Playgroud)

在查询和子查询中同时使用表时,您应该始终为两者设置别名,如下所示:

SELECT *
FROM some_table as t1
WHERE
 t1.relevant_field in
 (
  SELECT t2.relevant_field
  FROM some_table as t2
  GROUP BY t2.relevant_field
  HAVING COUNT ( t2.relevant_field ) > 1
 );
Run Code Online (Sandbox Code Playgroud)

这有帮助吗?