【问题标题】:SQL Server IN vs. EXISTS PerformanceSQL Server IN 与 EXISTS 性能
【发布时间】:2011-01-05 03:19:46
【问题描述】:

我很好奇以下哪种方法更有效?

我一直对使用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])

【问题讨论】:

  • 找出答案的最佳方法是尝试一下并进行一些测量。
  • 得到要为此做无数次重复......
  • @marc_s:是的,但我认为这确实取决于大小写。我想规范的答案是 klausbyskov 的(或任何可以声称现有技术的人)。
  • 仅供参考,如果您想要性能的方式,您可以在您的where exists 中使用select 1 from Base...,因为您实际上并不关心结果,只是一个行实际存在。
  • @brad 不是。编译器将忽略selectfrom 之间的任何内容(除非您在其中放置另一个子查询或类似的东西......奇怪)。 IMO,语法应该是semi join table2 on ....

标签: sql-server sql-server-2005 exists query-performance sql-in


【解决方案1】:

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

  1. 首先,相关子查询可能会导致子查询针对每一行运行。
  2. 在第二个示例中,子查询应该只运行一次,因为不相关。
  3. 在第二个示例中,IN 将在找到匹配项后立即短路。

【讨论】:

    【解决方案2】:

    我会选择 EXISTS 而不是 IN,请参阅下面的链接:

    SQL Server: JOIN vs IN vs EXISTS - the logical difference

    有一个常见的误解,即 IN 在返回结果方面与 EXISTS 或 JOIN 的行为相同。这根本不是真的。

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

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

    加入:在加入列上加入 2 个结果集。

    博客来源:https://stackoverflow.com/users/31345/mladen-prajdic

    【讨论】:

      【解决方案3】:

      EXISTS 会更快,因为一旦引擎找到命中,它就会退出寻找,因为条件已被证明是正确的。

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

      【讨论】:

      • 这曾经是正确的,但在当前版本(至少 2008 年)中,优化器要聪明得多……它实际上将 IN () 视为 EXISTS ()。
      • @Aaron - 是的,通常优化器会在内部产生更好的计划。但是,在更复杂的情况下,依赖内部快捷方式可能是有害的。
      • 奇怪。虽然我的一个查询的查询计划完全相同,但解释显示 IN 的行数为 972,EXIST 的行数为 959。其他一切都是一样的。这是在 Mysql 5.1.42 上,所以它可能只是旧的。
      • 这完全是错误的。那是在 2010 年,现在仍然如此。
      • IN 和 EXISTS 具有完全相同的查询计划和 IO。没有理由认为它们的性能不同。检查您的时间统计数据并证明自己
      【解决方案4】:

      在这些情况下,执行计划通常是相同的,但在您了解优化器如何影响索引等所有其他方面的因素之前,您永远不会知道。

      【讨论】:

        【解决方案5】:

        我已经在 SQL Server 2005 和 2008 上进行了一些测试,并且在 EXISTS 和 IN 上都返回了完全相同的实际执行计划,正如其他人所说的那样。优化器是最优的。 :)

        需要注意的是,如果您的查询措辞不当,EXISTS、IN 和 JOIN 有时会返回不同的结果:http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx

        【讨论】:

          【解决方案6】:

          要优化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
          • TOP 1 在与 EXISTS 一起使用时应该是完全无关的(或事件冗余)。 EXISTS 总是在找到任何匹配的行后立即返回。
          • 到目前为止,我没有看到这种方法对性能有任何好处。请展示一些执行计划的截图
          【解决方案7】:

          公认的答案是短视的,而且问题有点松散:

          1) 均未明确提及是否存在覆盖索引 左侧、右侧或两侧。

          2) 两者都没有考虑输入左侧集的大小和 输入右侧设置。
          (这个问题只是提到了一个整体的大结果集)。

          我相信优化器足够聪明,可以在由于 (1) 和 (2) 造成显着成本差异时在“in”与“exists”之间进行转换,否则它可能只是用作提示(例如存在鼓励在右侧使用可搜索索引)。

          这两种形式都可以在内部转换为连接形式,颠倒连接顺序,并作为循环、散列或合并运行——基于估计的行数(左和右)和左、右或两者中的索引存在侧面。

          【讨论】:

          • 优化器总是INEXISTS提供相同的计划。尝试提出他们没有得到相同计划的任何情况(尽管这不适用于NOT INNOT EXISTS
          • @MartinSmith 我假设你知道你在说什么,但你有任何证据证明计划总是一样的吗?如果是这样,这将消除长达十年的分歧。
          • @MarredCheese - 那些声称制作一个这样的例子是不同的人有责任
          【解决方案8】:

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

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

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

          考虑一下:

            1234563
          1. 如果您使用 EXISTS 并且与外部查询的连接很复杂(需要更多时间来执行,没有合适的索引),则查询会因外部表中的行数而变慢,有时是估计的时间完成可以在几天内。如果您的给定硬件可以接受行数,或者数据的基数正确(例如,大型数据集中的 DISTINCT 值较少)IN 可以比 EXISTS 执行得更快。

          2. 当您在每个表上有相当数量的行时,将注意到上述所有内容(公平地说,我的意思是超出您的 CPU 处理和/或缓存的 ram 阈值)。

            李>

          所以答案取决于它。您可以在 IN 或 EXISTS 中编写复杂的查询,但根据经验,您应该尝试将 IN 与一组有限的不同值和 EXISTS 一起使用,当您有很多行具有很多不同的值时。

          诀窍是限制要扫描的行数。

          问候,

          马里亚诺C

          【讨论】:

            【解决方案9】:

            这里有许多误导性的答案,包括高度赞成的答案(尽管我不相信他们的操作意味着伤害)。 简短的回答是:这些都是一样的。

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

            我们在调用[NOT] IN[NOT] EXISTS 时执行的关系(数学理论)运算是半联接(使用NOT 时是反联接)。相应的 sql-server 操作具有相同的名称,这并非巧合。在任何地方都没有提到INEXISTS 的操作——只有(反)半连接。因此,逻辑上等效的 INEXISTS 选择不会影响性能,因为只有一种方法,即(反)半连接执行操作来获得结果 .

            一个例子:

            查询 1 (plan)

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

            查询 2 (plan)

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

            【讨论】:

            • 你测试了吗?如果是这样,你能分享你的 SQL 和你的结果吗?
            • 测试了多次。我可以创建另一个测试用例,我会的,但测试用例并不意味着优化器将对具有不同统计信息的表执行完全相同的计划。这可能会导致有人认为答案是片面的——但不存在多个半连接运算符是事实。也许我会在某个地方找到一个列表并将其链接起来。
            猜你喜欢
            • 2015-10-01
            • 2020-06-30
            • 2011-06-11
            • 2010-11-15
            • 2021-11-03
            • 2012-11-21
            • 2018-02-18
            • 2011-01-11
            相关资源
            最近更新 更多