【问题标题】:SQL - query inside NOT IN takes longer than the complete query?SQL - NOT IN 中的查询比完整查询需要更长的时间?
【发布时间】:2011-01-05 22:58:29
【问题描述】:

我在我的 SQL 查询中使用 NOT IN。

例如:

select columnA 
from table1
where columnA not in (
select columnB
from table2)

这部分查询怎么可能

select columnB
from table2

需要 30 秒才能完成,但上面的整个查询需要 0.1 秒才能完成?? 完整的查询不应该花费 30 秒 + 吗?

顺便说一句,两个查询都返回有效结果。

谢谢!

评论回复

是不是因为第二个查询没有 实际完成但只有 返回前 'x' 行(out 一张非常大的桌子?)

不,查询在 30 秒后完成,返回的行数不多(例如 50)。

但@Aleksandar 想知道为什么 与表演有关的问题 杀手太快了。

完全是我的观点

还有选择不同的多长时间 table2中的columnB要执行吗?

其实原来的查询是“select distinct...

【问题讨论】:

  • 是不是因为第二个查询实际上还没有完成,而是只返回了前“x”行(在一个非常大的表中?)
  • table2 是否非常大,因为对列的正常选择需要 30 秒才能完成?
  • 你能看看两者的执行计划吗?
  • 或更好:发布两个计划以及两个表的 DDL。
  • @Aleksandar Tomic,您的问题的答案在于您拒绝向我们提供的细节。您需要意识到 SQL 本质上是声明性的。你指定你想要的“什么”,然后让数据库提出一个解决它的算法。现在,由于底层细节,数据库正在做一些有趣的事情。如果您向我们展示确切的查询、其中的解释计划以及所有引用表的创建脚本,这些详细信息将会显示出来。

标签: sql oracle


【解决方案1】:

您似乎认为您的主要查询意味着以下步骤:

(1)  Run the subquery
(2)  Check each row in table1 against the result set from the subquery.

因此,您认为单独运行子查询必须比运行整个查询花费更少的时间。

但 SQL 不是过程语言,查询的结构并不一定暗示执行查询将遵循的步骤。

正如 Guffa 所回答的那样,优化器将提出(它认为是)执行每个查询的最佳计划。从查询来看,这些执行计划并不总是显而易见的,在某些情况下确实可能非常违反直觉。

我认为,在这种情况下,优化器很可能提出了一种更快的方法来检查 table2 中是否存在值,而不是一次查询所有 table2。这可能是 Guffa 展示的转变(尽管这仍然不能告诉您正在使用的确切执行计划)。

我猜 table1 的行数明显少于 table2,并且 table2.columnB 上存在索引。所以它所要做的就是从 table1 中获取行,然后探测每个值的索引以检查是否存在。但这只是一种可能。

此外,正如 Michael Buen 所指出的,返回的结果集大小的差异也会影响您的感知性能。我的直觉是,这是执行计划差异的次要因素,但它可能很重要。

【讨论】:

  • 您的答案和迈克尔的答案几乎就是我想要的。我不希望有人准确指出问题出在哪里(这就是我简化查询的原因),但总体答案几乎没有上述可能性。我有很多其他方法可以使子查询更快地工作,但我想知道在 NOT IN (...) 内部时,慢速子查询如何“工作得更快”。感谢 Dave,也感谢所有其他人的回答。
【解决方案2】:

这是因为查询优化器将查询变成看起来完全不同的东西。实际查询应该与这样的查询产生的相同:

select columnA 
from table1
left join table2 on ColumnA = ColumnB
where ColumnB is null

如果数据库可以使用索引来连接表,也许它不必查询整个表2,甚至不必接触表本身。

【讨论】:

  • 关于访问索引而不是基表的评论是否同样适用于查询select columnB from table2? Oracle 中是否有某种索引适合一个查询但不适合另一个查询?
  • 如果 columnA 或 columnB 可以为空,则 Oracle 无法应用该转换。 NOT IN 成为带有子查询的过滤操作,而您的查询成为反 jon(散列或 nl)
  • @Martin:当您从表中选择所有内容时,数据库必须访问整个表(或整个索引),但是当您将它连接到另一个表时,它可能只需要访问一个表的一小部分,因为连接消除了大部分记录。
  • @Ronnis:是的,当然会有变化,具体取决于表格布局,还有数据量和变化程度。
  • 对不起,我认为我没有正确查看您的答案。特别是查询的最后一行。事实证明,OP 提出的问题并不是他看到的实际问题,但无论如何他想要解释的行为......
【解决方案3】:

一个戏剧性的比较,让我们这么说......

select columnB
from table2

...有十亿行(30 秒),许多数据通过网络传输并呈现给用户。

还有这个……

select columnA 
from table1

...只有一行。

如果您不打算显示 table2 的数据,RDBMS 不会将 table2 的数据从服务器拉到客户端。所以在做数据存在性测试时,不会涉及太多网络带宽或I/O,这一切都发生在服务器上,唯一会从服务器拉到客户端的就是table1的一行。

select columnA 
from table1
where columnA not in (
select columnB
from table2)

如果你的 columnA 和 columnB 恰好有索引,事情会特别快

会使数据库操作变慢的原因有两个:首先是当您从服务器向客户端提取过多数据时,其次是当您没有相关字段的索引时

【讨论】:

  • @Aleksandar 你说你的查询只返回 50 行。
  • @Aleksandar - 您能否发布您的实际查询和表定义,而不是过于简化的版本。
【解决方案4】:

当它可以利用索引并且返回结果的数量很少时。有可能。返回结果可能会导致执行时间。

【讨论】:

    【解决方案5】:

    只是插话,确保你知道NOT In and NOT EXISTS之间的区别。

    如果“columnA”为 NULL,则不会通过您正在查看的 NOT IN 解决方案返回,但已经呈现的 LEFT 反连接示例将表现为 NOT EXISTS。

    此外,请确保 TOAD/SQL Developer 不是“只显示他们喜欢做的前 50 个”(从 table1 中执行 select count(*) 以查看 50 是否确实是查询结果)。

    对查询执行 EXPLAIN PLAN 并查看它是否突出显示任何看起来令人震惊的内容 - 检查您的索引并查看列是否允许 NULLS - 缺少索引可能是罪魁祸首,但从 NULLS 进行全表扫描可能是导致噩梦)。

    【讨论】:

    • 谢谢哈里森,我知道你提到的所有事情。现在检查解释计划,看看它是否有帮助......
    【解决方案6】:

    NOT 是性能杀手。

    在某些 SQL 引擎上,首先将 in(...) 放入临时表,然后重新运行查询,对临时表中的数据执行 NOT。

    如果可以的话,你应该只使用 IN !

    【讨论】:

    • 但是@Aleksandar 想知道为什么性能杀手的问题会这么快。
    • NOT IN 可以非常高效。如果运算符两侧的列都定义为 NOT NULL,则 NOT IN 会打开非常有效的访问路径(HASH ANTI JOIN、NESTED LOOP ANTI JOIN...)。这可能是 OP 查询中发生的情况。想一想:如果一个特性总是不好,它就会被删除。
    • 可以是的,使用索引更好,但不是每次都好(也考虑一下)
    • 说“不是性能杀手”是 (a) 过度概括,并且 (b) 与所提出的实际问题无关。
    • 好吧,好吧,有时候不是性能杀手...stackoverflow.com/questions/3756361/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多