【问题标题】:SQL select IN (select) process too long why?SQL select IN(选择)过程太长为什么?
【发布时间】:2012-11-15 05:35:33
【问题描述】:

假设 TABLE1 中有 100 万个条目。

Table2 中有 50k 个条目。

SELECT stringVal 
FROM TABLE2 
WHERE idTable2=5

选择结果:

5
4

该选择需要0.02s 来处理 但是当我在 IN 中使用它时,它最多需要20.20s

SELECT count(*) 
FROM TABLE1 
WHERE stringVal IN (
   SELECT stringVal FROM TABLE2 where idTable2=5);

如果我像这样使用它,它会在 0.02 秒内处理完

SELECT count(*) 
FROM TABLE1 
WHERE stringVal IN (5,4);

谁能解释一下这里的工作原理?

【问题讨论】:

  • 哪个 RBDMS?试试 MySQL 的解释/SQL Server 的执行计划/等效项,看看发生了什么。

标签: sql


【解决方案1】:

我认为您的 RDBMS 在执行查询方面做得很差,其他 RDBMS(例如 SQL Server)可以看到,如果子查询与外部查询不相关,它将在内部实现结果并且不会重复执行子查询。例如

select *
    , (select count(*) from tbl) -- an smart RDBMS won't execute this repeatedly
from tbl

一个好的RDBMS不会重复执行计数,因为它是一个独立的查询(与外部查询无关)

尝试所有选项,反正只有几个选项

第一,试试 EXISTS。您的 RDBMS 的 EXISTS 可能比它的 IN 快。我遇到的 IN 比 EXISTS 快,例如:Why the most natural query(I.e. using INNER JOIN (instead of LEFT JOIN)) is very slow Quassnoi 的相同观察(IN 比 EXISTS 快):http://explainextended.com/2009/06/16/in-vs-join-vs-exists/

SELECT count(*) 
FROM TABLE1 
WHERE 
   -- stringVal IN 
   EXISTS(
       SELECT * -- please, don't bikeshed ;-)
       FROM TABLE2            
       where 
             table1.stringVal = table2.stringVal -- simulated IN
             and table2.idTable2 = 5);

第二,尝试INNER JOIN,如果没有重复就使用这个,或者使用DISTINCT删除重复。

SELECT count(*) 
FROM TABLE1 
JOIN (
       SELECT DISTINCT stringVal -- remove duplicates
       FROM TABLE2            
       where table2.idTable2 = 5 ) as x
ON X.stringVal = table1.stringVal

第三,尝试自己实现这些行。我在使用 SQL Server 时遇到了同样的问题,查询具体化的行比查询另一个查询的结果要快。

查看将查询结果具体化到表的示例,然后在结果上使用 IN。我发现它比在另一种查询方法上使用 IN 更快,您可以阅读帖子的底部:http://www.ienablemuch.com/2012/05/recursive-cte-is-evil-and-cursor-is.html

例子:

SELECT distinct stringVal -- remove duplicates
into anotherTable
FROM TABLE2            
where idTable2 = 5;

SELECT count(*) 
FROM TABLE1 where stringVal in (select stringVal from anotherTable);

上面是在Sql Server和Postgresql上工作的,在其他RDBMS上可能是这样的:

create table anotherTable as
SELECT distinct stringVal -- remove duplicates
FROM TABLE2            
where table2.idTable2 = 5;


select count(*)
from table1 where stringVal in (select stringVal from anotherTable)

【讨论】:

  • 你的第二个例子工作得很好,大约需要 0.2 秒来处理。感谢那些每天学习新事物的例子:)
【解决方案2】:

虽然我喜欢子查询,但它们非常强大,但速度也很慢,因为每次迭代都必须对查询进行完全评估,哎呀! (取决于实现)

这就是为什么他们是我的/我们最后的手段。

一些 SQL 实现非常好,并且会缓存子查询,虽然我不太确定这有多安全,但你仍然必须遍历整个结构,如果结构没有正确优化,则需要二次甚至三次时间如果你嵌套了足够多的它们......

SELECT stringVal 
FROM TABLE2 
WHERE idTable2=5

这是线性时间 O(n),如果 sql 数据库存储统计信息,它甚至可以是常数 O(1),但我们假设它不是这样,它将搜索每一行并返回所有那些匹配 where 子句。

SELECT count(*) 
FROM TABLE1 
WHERE stringVal IN (
   SELECT stringVal FROM TABLE2 where idTable2=5);

假设子查询没有缓存,那么它在每一行都被评估,如果你有很多评估,很多浪费的重复计算,即使它的缓存结构也可能不是搜索的最佳选择,更不用说您还在字符串列表上比较字符串。

SELECT count(*) 
FROM TABLE1  
WHERE stringVal IN (5,4);

子查询仍在评估中,但它是一个常量表达式,基本上没有任何开销,它不需要做任何 IO 或处理锁或任何事情 :)

【讨论】:

  • 这就是我所担心的,在每次迭代时它都需要重新进行选择,因为它没有缓存。这可能是这里的问题。
  • 是的,它依赖于实现,一些缓存它,另一些不缓存,缓存非常危险,因为如果表被更新,那么你可以返回错误数据,你可能有一个锁,但这确实使事情复杂化.
  • 我一直对数据库设计很着迷,它实际上很难与开发编译器或构建内核相提并论...... :) 好帖子。
【解决方案3】:

试试这个

SELECT count(*) FROM TABLE1 where EXISTS
(SELECT 1 FROM TABLE2 where idTable2=5 and stringVal = TABLE1.stringVal  );

您应该为 TABLE1 和 TABLE2 表的 stringVal 创建索引。

【讨论】:

    【解决方案4】:

    这是一个简单的连接,可以为您提供与您正在寻找的结果相同的结果。这可以应用于许多不同的情况,这样可以避免查询另一个表。

    SELECT COUNT(*)
    FROM TABLE1 INNER JOIN TABLE2 ON TABLE1.'COLUMN' = TABLE2.'COLUMN' AND TABLE2.IDTABLE2=5 
    WHERE 'WHATEVER YOU WANT'
    

    将“COLUMN”替换为两个表中都引用的列,通常是 ID 或主键。

    【讨论】:

      猜你喜欢
      • 2013-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-24
      • 1970-01-01
      • 2018-07-22
      • 1970-01-01
      • 2021-06-28
      相关资源
      最近更新 更多