SQL Server IN vs. EXISTS性能


115

我很好奇以下哪个会更有效?

我一直对使用它持谨慎态度,IN因为我相信SQL Server会将结果集变成一个大IF语句。对于较大的结果集,这可能会导致性能下降。对于较小的结果集,我不确定哪一个都更好。对于大型结果集,会不会EXISTS更有效率?

WHERE EXISTS (SELECT * FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

WHERE bx.BoxID IN (SELECT BoxID FROM Base WHERE [Rank = 2])

8
找出答案的最佳方法是尝试一下并进行一些测量。
克劳斯·拜斯科夫·皮德森

10
还有的来是这一个极大的重复......
marc_s

5
@marc_s-大概是这样,但是当我需要仔细阅读有关该主题的所有帖子并找到适合我的情况时,我对问题有四个答案。
兰迪·明德

7
仅供参考,如果您想要高效的方法,则可以使用select 1 from Base...where exists因为您实际上并不关心结果,只是实际上存在一行。
布拉德(Brad)

2
@marc_s真的很可悲,因为我确实花时间浏览这些帖子,以便不向stackoverflow添加更多垃圾。我不需要量身定制的答案即可完成工作。那就是那种添加了Gazillion重复项的想法,而不是只有几个很好的答案
IvoC 2012年

Answers:


140

EXISTS 之所以会更快,是因为一旦发现碰撞,发动机就会退出,因为情况证明是正确的。

使用IN,它将在进一步处理之前从子查询中收集所有结果。


4
那是个很好的观点。IN语句要求SQL Server生成完整的结果集,然后创建我认为的大IF语句。
Randy Minder 2010年

72
过去确实如此,但在当前版本(至少2008年)中,优化器更加智能……实际上,它像INISTS()一样对待IN()。
亚伦·贝特朗

11
@Aaron-是的,通常优化器会在内部产生更好的计划。但是,在更复杂的情况下,依赖内部快捷方式可能是有害的。
Scott Coates

2
这是完全错误的。那是在2010年,现在仍然如此。
马格努斯

2
IN和EXISTS具有完全相同的查询计划和IO。没有理由认为它们在性能上有所不同。检查您的时间统计信息并完善自己
-Nelssen

40

可接受的答案是短视的,问题有点松散:

1)都没有明确提及左侧,右侧或两侧是否存在覆盖索引。

2)都没有考虑输入左侧集和输入右侧集的大小。
(该问题仅提及总体上较大的结果集)。

我相信,当由于(1)和(2)造成巨大的成本差异时,优化器足够聪明,可以在“输入”与“存在”之间进行转换,否则它可能仅用作提示(例如,存在鼓励使用右侧的可检索索引)。

两种形式都可以转换为内部联接形式,颠倒联接顺序,并根据估计的行数(左右)和左侧,右侧或两侧的索引存在情况以循环,哈希或合并的方式运行。


3
不知道为什么这个出色的答案没有得到更多关注。我同意理解双方的指数/结构可能会产生影响。说得好。
SheldonH 2015年

优化器总是 针对IN和给出相同的计划EXISTS。尝试提出任何他们没有得到相同计划的情况(尽管这不适用于NOT INNOT EXISTS
Martin Smith,

@MartinSmith我假设您知道您在说什么,但是您有任何证据证明这些计划始终是相同的吗?如果是这样,它将消除长达十年之久的分歧。
MarredCheese

@MarredCheese -的责任是人们要求它是不同的,以产生该单一的例子
马丁·史密斯


5

这里有许多误导性的答案,包括被高度评价的答案(尽管我不认为他们的操作会带来伤害)。简短的答案是:这些是相同的。

(T-)SQL语言中有很多关键字,但是最后,真正在硬件上发生的唯一事情就是执行查询计划中看到的操作。

当我们调用我们做的关系(数学理论)操作[NOT] IN[NOT] EXISTS为半连接(当使用反连接NOT)。相应的sql-server操作具有相同的名称并非巧合。没有任何提及IN或提及的操作EXISTS-仅(反)半联接。因此,逻辑上等价的INvs EXISTS选择不会影响性能,因为(反)半联接执行操作只有一种方法来获得结果

一个例子:

查询1(计划

select * from dt where dt.customer in (select c.code from customer c where c.active=0)

查询2(计划

select * from dt where exists (select 1 from customer c where c.code=dt.customer and c.active=0)

你测试过了吗?如果是这样,您可以共享您的SQL和结果吗?
UnhandledExcepSean

测试了多次。我可以创建另一个测试用例,但是可以,但是一个测试用例并不意味着优化器将对具有不同统计信息的表执行完全相同的计划。这可能会使某人认为答案是部分的-但事实是多个半联接运算符不存在。也许我会在某个地方找到一个列表并将其链接。
George Menoutis

5

我将通过EX使用EXISTS,请参见以下链接:

SQL Server:JOIN vs IN vs EXISTS-逻辑差异

常见的误解是,就返回结果而言,IN与EXISTS或JOIN的行为相同。这是不正确的。

IN:如果指定值与子查询或列表中的任何值匹配,则返回true。

存在:如果子查询包含任何行,则返回true。

联接:在联接列上联接2个结果集。

博客信誉:https : //stackoverflow.com/users/31345/mladen-prajdic


哇,谢谢您的博客和解释。
ChristianMüller

3

在这些情况下,执行计划通常是相同的,但是直到您看到优化器如何将索引等所有其他方面都考虑在内之后,您才真正知道。


3

因此,IN与EXISTS不同,也不会产生相同的执行计划。

通常,EXISTS用于关联子查询中,这意味着您将EXISTS内部查询与外部查询联接在一起。这将增加更多的步骤来生成结果,因为您需要解决外部查询联接和内部查询联接,然后匹配它们的where子句以将两者都联接。

通常使用IN时无需将内部查询与外部查询相关联,并且仅一步即可解决(在最佳情况下)。

考虑一下:

  1. 如果您使用IN并且内部查询结果是数百万行不同的值,则在EXISTS查询性能出色(具有与外部查询联接的正确索引)的情况下,它可能会比EXISTS慢。

  2. 如果您使用EXISTS,并且与外部查询的连接很复杂(需要花费更多的时间来执行,没有合适的索引),则会使查询的速度减慢外部表中的行数,有时估计的完成时间可能是几天。如果行数对于给定的硬件是可接受的,或者数据的基数正确(例如,大数据集中的DISTINCT值较少),则IN的执行速度可能比EXISTS快。

  3. 当您在每个表上都有相当数量的行时,以上所有内容都会被注意(公平地讲,我的意思是超出您的CPU处理和/或缓存的ram阈值)。

因此,答案是否取决于。您可以在IN或EXISTS内编写一个复杂的查询,但是根据经验,当行数很多且值不同时,应尝试使用IN的有限一组不同值和EXISTS。

技巧是限制要扫描的行数。

问候,

马里亚诺


1

为了优化 EXISTS,请非常直白;只是需要一些东西,但是实际上您不需要从相关子查询返回的任何数据。您只是在评估布尔条件。

所以:

WHERE EXISTS (SELECT TOP 1 1 FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

因为相关子查询为RBAR,所以第一个结果命中使条件成立,因此不再进行处理。


在使用LEFT JOIN + NULL编码时,我始终会非常谨慎,因为如果您对NULL的处理不十分小心,很容易错过或偏斜结果。我很少遇到这样的情况,即EXISTS或CTE(用于查找重复项,或针对丢失的数据进行合成插入)不能同时满足相同的要求,并且不能胜过LEFT JOIN + NULL
Josh Lewis,

3
当与EXISTS一起使用时,TOP 1应该是完全无关的(或事件冗余的)。找到所有匹配的行后,EXISTS总是返回。
卡尔·基宁格

到目前为止,我没有发现此方法对性能有任何好处。请显示执行计划的某些屏幕快照
DaFi4 '16

-1

不在我的头上,并且不能保证是正确的:我相信在这种情况下,第二个会更快。

  1. 首先,相关子查询可能会导致子查询针对每一行运行。
  2. 在第二个示例中,子查询应该只运行一次,因为它不相关。
  3. 在第二个示例中,IN将在找到匹配项后立即将其短路。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.