【问题标题】:Will "WHERE x IN (SELECT value FROM table) " be inefficient for large tables?“WHERE x IN (SELECT value FROM table)”对于大表会不会低效?
【发布时间】:2012-12-29 23:15:08
【问题描述】:

以下两个 SQL 查询返回相同的结果:

    SELECT * FROM Table1
    WHERE Table1.Value1 = 'something' OR Table1.Value2 IN (SELECT Value2 FROM Table2)

    SELECT * FROM Table1
    LEFT JOIN Table2 
    ON Table1.Value2 = Table2.Value2
    WHERE (Table1.Value1 = 'something' OR Table2.Value2 IS NOT NULL)

同样,这两个查询返回相同的结果:

    SELECT * FROM Table1
    WHERE Table1.Value1 = 'something' AND Table1.Value2 NOT IN (SELECT Value2 FROM Table2)

    SELECT * FROM Table1
    LEFT JOIN Table2
    ON Table1.Value2 = Table2.Value2
    WHERE Table1.Value1 = 'something' AND Table2.Value2 IS NULL

就个人而言,我发现使用“IN”或“NOT IN”的选项更容易阅读(特别是因为我的实际查询中已经有一堆连接)。但是,如果 Table2 中的值越来越多(目前只返回三个结果),这个查询会不会变得低效?或者查询优化器会在幕后把它找出来并把它变成一个连接吗?我正在使用 SQL Server 2012。

【问题讨论】:

  • 对于第一个查询,WHERE EXISTS 将比您提供的两个选项更有效(或者在最坏的情况下,效率不会更低)。有关第二种选择的非常彻底的处理,请参阅sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-join
  • @AaronBertrand:您提供的链接描述了ANTI JOIN,而不是SEMI JOIN。这些是完全不同的。
  • @Quassnoi 问题中的第二个代码示例谈到了ANTI SEMI JOIN
  • @AaronBertrand:你是对的,我的错!

标签: sql-server sql-server-2012 where-in


【解决方案1】:
  • 这两个查询不等价: 当使用 IN(或 NOT IN)时,对于 Table1 中的每一行,您将获得 0 或 1 个结果行。 使用 join 时,每一行可能出现 0 次、1 次或多次。 所以,“两个 SQL 查询返回相同的结果”——只是因为特定的数据。 或者 Table2 在 Value2 上有唯一的索引/PK

  • 将 UNION 用作

SELECT ... WHERE Table1.Value1 = 'something'
UNION (ALL)
SELECT ... WHERE Table1.Value2 = Table2.Value2

也可能会给出不同的结果,因为 UNION 将删除重复项(这可能很有价值),如果 UNION ALL 与两个条件都匹配,则它们可能会使某些结果行加倍

  • 如果您将在第一个查询中使用 EXISTS() 而不是 IN()... 很可能你会得到相同的执行计划,因为 sql 优化器会识别出这些操作是相等的并且会选择相同的最优方式。

  • 即使您可以在语句中使用子查询,sql 优化器也可能会以不使用子查询的方式重建计划。换句话说,两个相同的查询,不同的写法,很可能会优化到同一个计划。

  • 对于非常复杂的查询可能无法正常工作,因为sql可能没有足够的时间来完全完成优化,并在它停止的地方停止。在这种情况下,这种不同但相似的查询可能会产生不同的结果。你需要尝试和测试。

计划和性能将取决于数据、参数类型(常量、变量、计算值)、统计信息、索引......对于这些标准的某些组合,Query-1 将比 Query-2 更优化,并且虎钳- 对其他人也是如此。

要获得正确答案,您需要分析和比较执行计划

【讨论】:

    【解决方案2】:

    第一个会更好:

    SELECT <cols> 
      FROM dbo.Table1
      WHERE Value1 = 'something' 
      OR EXISTS (SELECT 1 FROM dbo.Table2 WHERE Value2 = Table1.Value2);
    

    虽然您的性能问题 - 假设 Value2 在两个位置都被索引,并且您实际上只选择您需要的列而不是使用 * 强制扫描或查找 - 将是 OR。如果 Value1 被正确索引,您可能会考虑这种替代方案,至少可以测试性能差异(您需要查看计划,而不仅仅是测量时间,而您只有三行):

    SELECT <cols>
      FROM dbo.Table1 
      WHERE Value1 = 'something'
    UNION ALL
    SELECT <cols>
      FROM dbo.Table1
      WHERE Value1 <> 'something'
      AND EXISTS (SELECT 1 FROM dbo.Table2 WHERE Value2 = Table1.Value2);
    

    对于 NOT IN 查询,这将更可靠,至少与您提供的两个选项一样有效:

    SELECT <cols>
      FROM dbo.Table1
      WHERE Value1 = 'something' 
      AND NOT EXISTS (SELECT 1 FROM dbo.Table2 WHERE Value2 = Table1.Value2);
    

    索引在这里将是关键,但重要的是要了解 NOT IN 和 LEFT OUTER JOIN 可能会让您陷入困境。请参阅以下文章:

    http://www.sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-join

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-12
      • 2011-04-06
      • 1970-01-01
      • 1970-01-01
      • 2018-02-20
      • 2017-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多