有关SQL Server优化子查询与加入的问题

时间:2010-03-01 22:38:05

标签: sql sql-server tsql

这些查询中的哪一个更有效率,现代DBMS(如SQL Server)是否会进行更改以使它们相等?

SELECT DISTINCT S# 
  FROM shipments 
 WHERE P# IN (SELECT P# 
                FROM parts 
               WHERE color = ‘Red’)

VS

SELECT DISTINCT S# 
  FROM shipments, parts 
 WHERE shipments.P# = parts.P# 
   AND parts.color = ‘Red’

4 个答案:

答案 0 :(得分:6)

满足您对此类事情的好奇心的最好方法是启动Management Studio并查看执行计划。您还需要查看SQL事件探查器。正如我的一位教授所说:“编译器是最终的权威。”当您想要了解SQL Server中查询的性能配置文件时,就会有类似的理念 - 只需看。

从这里开始,此答案已更新

实际比较可能非常有启发性。例如,在我刚刚进行的测试中,我发现任何一种方法都可能产生最快的时间,具体取决于查询的性质。例如,查询表单:

Select F1, F2, F3 From Table1 Where F4='X' And UID in (Select UID From Table2)

在Table1上产生表扫描,在表2上进行单纯的索引扫描,然后是右半连接。

表格的查询:

Select A.F1, A.F2, A.F3 From Table1 A inner join Table2 B on (A.UID=B.UID) 
  Where A.Gender='M'

产生了相同的执行计划,但有一点需要注意:哈希匹配这次是一个简单的右连接。所以这是首先要注意的事项:执行计划并没有太大的不同。

这些不是重复的查询,因为第二个可能会返回多个相同的记录(表2中的每个记录一个)。这里令人惊讶的是性能:子查询比内连接要快得多。对于千位数的数据集(谢谢Red Gate SQL数据生成器),内部连接速度慢40倍。我很震惊。

好的,真正的苹果到苹果怎么样? 是匹配的内部联接 - 请注意将重复项删除的额外步骤:

Select Distinct A.F1, A.F2, A.F3 From Table1 A inner join Table2 B 
  on (A.UID=B.UID) 
  Where A.Gender='M'

执行计划确实发生了变化,因为还有一个额外的步骤 - 内部联接之后的排序。但奇怪的是,时间急剧下降,使得两个查询几乎完全相同(在五个试验中的两个中,内连接的速度非常快)。现在,我可以想象第一个内连接(没有“不同”)只是因为更多数据被转发到查询窗口这一事实 - 但它只是两倍(每个Table1记录有两个Table2记录) )。我没有很好的解释为什么第一个内连接速度要慢得多。

使用子查询向表2中的搜索添加谓词时:

Select F1, F2, F3 From Table1 Where F4='X' And UID in 
    (Select UID From Table2 Where F1='Y')

然后将索引扫描更改为聚簇索引扫描(这是有道理的,因为UID字段在我正在使用的表中有自己的索引),并且它占用的时间百分比上升。还添加了Stream Aggregate操作。果然,这确实会减慢查询速度。但是,计划缓存显然会起作用,因为查询的第一次运行显示出比后续运行更大的效果。

使用内部联接添加谓词时,整个计划的变化非常显着(作为练习留给读者 - 这篇文章足够长)。同样,性能与子查询的性能几乎相同 - 只要包含“Distinct”即可。与第一个例子类似,省略不同导致完成时间显着增加。

最后一件事:有人建议(现在你的问题包括)对表格的查询:

Select Distinct F1, F2, F3 From table1, table2
Where (table1.UID=table2.UID) AND table1.F4='X' And table2.F1='Y'

此查询的执行计划类似于内部联接的执行计划(在table2上的原始表扫描和合并连接之后有一个排序,而不是两个表的散列连接)。两者的表现也是可比的。我可能需要一个更大的数据集来梳理差异,但到目前为止,我没有看到这个构造或“存在”构造的任何优势。

所有这一切 - 您的结果可能会有所不同。我无法覆盖您在进行上述测试时可能遇到的各种查询。正如我在开始时所说,SQL Server附带的工具是你的朋友:使用它们。

那么:为什么选择一个而不是另一个呢?这实际上取决于您的个人偏好,因为在我测试的示例范围内,就时间复杂度而言,内部连接似乎没有优势。

在大多数经典查询案例中,我使用内部联接只是因为我“长大”了它们。但是,我在两种情况下使用子查询。首先,使用子查询更容易理解一些查询:表之间的关系是显而易见的。第二个也是最重要的原因是,我经常处于从我的应用程序中动态生成SQL的位置,并且子查询几乎总是更容易从代码中自动生成。

因此,最重要的是,最佳解决方案是使您的开发最有效的解决方案。

答案 1 :(得分:2)

使用IN更具可读性,我建议使用ANSI-92而非ANSI-89连接语法:

SELECT DISTINCT S#
  FROM SHIPMENTS s
  JOIN PARTS p ON p.p# = s.p#
              AND p.color = 'Red'

检查您的解释计划,看看哪个更好,因为它取决于数据和表设置。

答案 2 :(得分:2)

如果您没有从表格中选择任何内容,我会使用EXISTS条款。

SELECT DISTINCT S# 
FROM shipments a
WHERE EXISTS (SELECT 1
              FROM parts b
              WHERE b.color = ‘Red’
                AND a.P# = b.P#)

这将优化为与您发布的第二个相同。

答案 3 :(得分:1)

SELECT DISTINCT S# 
FROM shipments,parts 
WHERE shipments.P# = parts.P# and parts.color = ‘Red’;

使用IN强制SQL Server不对该列使用索引,子查询通常较慢