【问题标题】:SQL Server 2005 Performance: Distinct or full table in WHERE IN statementSQL Server 2005 性能:WHERE IN 语句中的不同表或完整表
【发布时间】:2008-10-08 09:25:38
【问题描述】:

我们有两个表:

  • 文档:id、title、document_type_id、showon_id
  • 文档类型:id、名称
  • 关系:DocumentType 有许多文档。 (Document.document_type_id = DocumentType.id)

我们希望检索给定 ShowOn_Id 的所有文档类型的列表。

我们看到了两种可能性:

SELECT DocumentType.*
FROM DocumentType
WHERE DocumentType.id IN (
    SELECT DISTINCT Document.document_type_id FROM Document WHERE showon_id = 42
);

SELECT DocumentType.*
FROM DocumentType
WHERE DocumentType.id IN (
    SELECT Document.document_type_id FROM Document WHERE showon_id = 42
);

我们的问题是:何时以及是否使用 DISTINCT 获取较小的记录集与检索整个表和 IN 语句将表带到第一个匹配项更好。 (我们猜这就是它的作用;-))

这对于不同的数据库是否不同,有共同的答案吗?

或者有更好的方法吗? (我们在 .NET 领域)

【问题讨论】:

    标签: sql-server database performance


    【解决方案1】:

    您可以使用连接:

    SELECT DISTINCT DocumentType.*
    FROM DocumentType
    INNER JOIN Document
    ON DocumentType.id=Document.document_type_id
    WHERE Document.showon_id = 42
    

    我认为这是最好的方法。

    【讨论】:

    • 谢谢,这可能是解决问题的最简单方法...这个查询是否也比 IN 子查询快?
    • 正如 ligged78 指出的那样,您也发现了,如果查询计划返回不同的东西,我会非常惊讶。毕竟,性能必须基于您的表/索引设计,而不是您的查询语法。
    【解决方案2】:

    为了获得最佳性能,您应该使用:

    SELECT DISTINCT dt.* 
    FROM 
        DocumentType dt
        INNER JOIN Document d ON dt.id=d.document_type_id and d.showon_id = 42
    

    联接在桥接多个表方面非常有效,因为 Where 子句中的嵌套查询需要执行单独的结果选择,以过滤 From 子句的结果。 join 语句也更具可读性。

    除了主键和外键关系之外,我还会在 showon_id 上放置一个索引。

    我的答案与 wmasm 的答案不同,只是将 showon_id 过滤器向上移动到内部连接。对于 MS SQL 2k5,我认为解释器足够聪明,可以自动执行此操作,但您总是希望使用尽可能小的结果集。在将多个表连接在一起时,将过滤器提升到内部连接语句可以限制查询必须处理的行数。如果你这样做,你应该明白这发生在每个行比较中,所以复杂的过滤器(例如 x = '%a' 或函数调用)最好留给 Where 子句,以便内部连接可以过滤掉不必要的比较.

    【讨论】:

      【解决方案3】:

      使用 EXISTS。它有时更快,但在我看来,它比 DISTINCT 和 JOIN 更具可读性。只是为了好玩,请回复此查询的查询计划和上面的 JOIN,看看是否有任何不同(它们可能被优化为相同的计划)。如果它们相同,我建议使用 EXISTS,因为它比 JOIN 更接近“普通语言”描述(因为您不想要来自 Document 等的任何数据)

      SELECT whatever
        FROM DocumentType dt
       WHERE EXISTS( SELECT *
                       FROM Document 
                      WHERE dt.id     = document_type_id
                        AND showon_id = 42)
      

      要获取查询计划(参考:http://msdn.microsoft.com/en-us/library/ms180765(SQL.90).aspx),请执行以下操作:

      SET SHOWPLAN_TEXT ON
      GO
      
      SELECT ...
      GO
      

      【讨论】:

        【解决方案4】:

        从我的角度来看,它在 SQL Server 内部应该没有任何区别(但谁知道这是如何实现的)。

        这样想:要返回结果集,服务器需要进入 Document 表并检索所有 document_type_id WHERE showon_id = 42。在检索 document_type_ids 的过程中(例如通过索引搜索),它会将它们放入哈希中桌子。当这个过程完成时,哈希表将包含不同的值。之后,查询执行进入 Document_Type 表,扫描主键并探测哈希表。请注意,这取决于,例如也许不使用哈希表会更有效,当来自 Document 表的预期行数与 Document_Type 相比较低时,但通常您会得到与刚刚建议的查询 wmasm 相同的查询计划。

        【讨论】:

        • 这是有道理的。是的,两个查询的结果集应该相同。所以问题是:服务器是否自己过滤掉重复值(并且过滤是否会对性能产生影响?),如果是,他在使用 DISTINCT 时是否以同样的方式执行此操作? (-> 相同的速度)
        • 您可以尝试衡量所有类型查询的数据性能。有关工具,请参阅 datamanipulation.net/SQLQueryStress
        【解决方案5】:

        关注Matt's answer

        我已启用查询计划并测试了到目前为止出现的以下四种不同的查询:

        • SELECT DocumentType.* FROM DocumentType WHERE DocumentType.id IN (SELECT DISTINCT Document.document_type_id FROM Document WHERE showon_id = 42);

        • SELECT DocumentType.* FROM DocumentType WHERE DocumentType.id IN (SELECT Document.document_type_id FROM Document WHERE showon_id = 42);

        • SELECT DISTINCT DocumentType.* FROM DocumentType INNER JOIN Document ON DocumentType.id=Document.document_type_id WHERE Document.showon_id = 42;

        • SELECT DocumentType.* FROM DocumentType WHERE EXISTS ( SELECT * FROM Document WHERE DocumentType.id=Document.document_type_id AND showon_id = 42);

        所有四个查询的查询计划结果是相同的:

         |--Hash Match(Right Semi Join, HASH:([Document].[document_type_id])=([DocumentType].[Id]))
               |--Hash Match(Inner Join, HASH:([Document].[Title], [Uniq1005])=([Document].[Title], [Uniq1005]), RESIDUAL:([Document].[Title] as [Document].[Title] = [Document].[Title] as [Document].[Title] AND [Uniq1005] = [Uniq1005]))
               |    |--Index Seek(OBJECT:([Document].[IX_Document_3] AS [Document]), SEEK:([Document].[showon_id]=(1)) ORDERED FORWARD)
               |    |--Index Scan(OBJECT:([Document].[IX_Document_1] AS [Document]))
               |--Table Scan(OBJECT:([DocumentType] AS [DocumentType]))
        

        我不确定每一行和每一个元素的含义,但从性能的角度来看,对于这类问题如何构造查询似乎并不重要......

        【讨论】:

          猜你喜欢
          • 2011-01-11
          • 1970-01-01
          • 2013-07-08
          • 1970-01-01
          • 2021-12-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多