Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 为什么联接子句的顺序会影响SQL Server中的查询计划?_Sql Server_Sql Server 2005_Optimization_Join_Sql Server 2000 - Fatal编程技术网

Sql server 为什么联接子句的顺序会影响SQL Server中的查询计划?

Sql server 为什么联接子句的顺序会影响SQL Server中的查询计划?,sql-server,sql-server-2005,optimization,join,sql-server-2000,Sql Server,Sql Server 2005,Optimization,Join,Sql Server 2000,我正在SQLServer2000(和2005)中构建一个视图,我注意到join语句的顺序会极大地影响查询的执行计划和速度 select sr.WTSASessionRangeID, -- bunch of other columns from WTSAVW_UserSessionRange us inner join WTSA_SessionRange sr on sr.WTSASessionRangeID = us.WTSASessionRan

我正在SQLServer2000(和2005)中构建一个视图,我注意到join语句的顺序会极大地影响查询的执行计划和速度

select      sr.WTSASessionRangeID,
            -- bunch of other columns
from        WTSAVW_UserSessionRange us
inner join  WTSA_SessionRange sr on sr.WTSASessionRangeID = us.WTSASessionRangeID
left outer join WTSA_SessionRangeTutor srt on srt.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionRangeClass src on src.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionRangeStream srs on srs.WTSASessionRangeID = sr.WTSASessionRangeID
--left outer join MO_Stream ms on ms.MOStreamID = srs.MOStreamID
left outer join WTSA_SessionRangeEnrolmentPeriod srep on srep.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionRangeStudent stsd on stsd.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionSubrange ssr on ssr.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionSubrangeRoom ssrr on ssrr.WTSASessionSubrangeID = ssr.WTSASessionSubrangeID
left outer join MO_Stream ms on ms.MOStreamID = srs.MOStreamID
在SQLServer2000上,上面的查询始终生成一个成本为946的计划。如果我取消了MyStand在查询的中间加入,并注释出底部的一个,则成本下降到263。执行速度相应下降。我一直认为查询优化器可以在不考虑连接顺序的情况下适当地解释查询,但顺序似乎很重要

既然顺序似乎很重要,我是否应该遵循一种连接策略来编写更快的查询

(顺便提一下,在SQL Server 2005上,使用几乎相同的数据,查询计划成本分别为0.675和0.631。)

编辑:在SQL Server 2000上,以下是分析的统计信息:

  • 946成本查询:9094ms CPU,5121读取,0写入,10123ms持续时间
  • 263成本查询:172ms CPU,7477读取,0写入,170ms持续时间
编辑:以下是表格的逻辑结构

SessionRange ---+--- SessionRangeTutor
                |--- SessionRangeClass
                |--- SessionRangeStream --- MO_Stream
                |--- SessionRangeEnrolmentPeriod
                |--- SessionRangeStudent
                +----SessionSubrange --- SessionSubrangeRoom
编辑:感谢Alex和gbn为我指明了正确的方向。我也发现了

以下是新的查询:

select sr.WTSASessionRangeID    // + lots of columns

from WTSAVW_UserSessionRange us
inner join WTSA_SessionRange sr on sr.WTSASessionRangeID = us.WTSASessionRangeID
left outer join WTSA_SessionRangeTutor srt on srt.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionRangeClass src on src.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionRangeEnrolmentPeriod srep on srep.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join WTSA_SessionRangeStudent stsd on stsd.WTSASessionRangeID = sr.WTSASessionRangeID

// SessionRangeStream is a many-to-many mapping table between SessionRange and MO_Stream
left outer join (
    WTSA_SessionRangeStream srs
    inner join MO_Stream ms on ms.MOStreamID = srs.MOStreamID
) on srs.WTSASessionRangeID = sr.WTSASessionRangeID

// SessionRanges MAY have Subranges and Subranges MAY have Rooms
left outer join (
    WTSA_SessionSubrange ssr    
    left outer join WTSA_SessionSubrangeRoom ssrr on ssrr.WTSASessionSubrangeID = ssr.WTSASessionSubrangeID
) on ssr.WTSASessionRangeID = sr.WTSASessionRangeID

SQLServer2000成本:24.9

显然,SQLServer2005优化器比SQLServer2000优化器好得多

然而,你的问题有很多道理。外部连接将导致执行根据顺序发生巨大变化(内部连接往往会优化到最有效的路径,但同样,顺序很重要)。如果你想一想,当你建立左连接时,你需要弄清楚左边到底是什么。因此,必须先计算每个联接,然后才能进行其他联接。它是连续的,而不是平行的。现在,很明显,您可以做一些事情来解决这个问题(例如索引、视图等)。但是,关键是:表需要知道左边是什么,然后才能执行左外部联接。如果你只是不断地添加连接,你会越来越抽象到什么,确切地说是在左边(特别是如果你使用连接表作为左边的表!)


但是,对于内部联接,您可以将它们并行化很多,因此就顺序而言,差别不大。

这取决于对哪些联接字段进行了索引-如果必须对第一个字段进行表扫描,但对第二个字段使用索引,则速度较慢。如果您的第一个连接字段是索引,则会更快。我的猜测是,通过确定索引字段并首先执行这些字段,2005可以更好地优化它。优化包含联接的查询的一般策略是查看您的数据模型和数据,并尝试确定哪些联接将最快地减少必须考虑的记录数。必须考虑的记录越少,查询运行的速度就越快。服务器通常也会生成更好的查询计划


在进行上述优化的同时,请确保联接中使用的任何字段都在几年前的DevConnections上编制索引。SQL Server性能会话指出:(a)外部联接的顺序很重要,以及(b)当查询有大量联接时,在确定计划之前不会查看所有联接。如果您知道您有可以帮助加快查询速度的联接,那么它们应该位于“发件人”列表的最早位置(如果可以)。

我不同意前面的所有答案,原因很简单:如果您更改左联接的顺序,您的查询在逻辑上是不同的,因此它们会生成不同的结果集。你自己看看:

SELECT 1 AS a INTO #t1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4;

SELECT 1 AS b INTO #t2
UNION ALL SELECT 2;

SELECT 1 AS c INTO #t3
UNION ALL SELECT 3;

SELECT a, b, c 
FROM #t1 LEFT JOIN #t2 ON #t1.a=#t2.b
  LEFT JOIN #t3 ON #t2.b=#t3.c
ORDER BY a;

SELECT a, b, c 
FROM #t1 LEFT JOIN #t3 ON #t1.a=#t3.c
  LEFT JOIN #t2 ON #t3.c=#t2.b
ORDER BY a;

a           b           c
----------- ----------- -----------
1           1           1
2           2           NULL
3           NULL        NULL
4           NULL        NULL

(4 row(s) affected)

a           b           c
----------- ----------- -----------
1           1           1
2           NULL        NULL
3           NULL        3
4           NULL        NULL

无论如何,您的查询可能是错误的。亚历克斯是对的。Eric可能也是正确的,但这个查询是错误的

让我们以这个子集为例:

WTSA_SessionRange sr
left outer join
WTSA_SessionSubrange ssr on ssr.WTSASessionRangeID = sr.WTSASessionRangeID
left outer join
WTSA_SessionSubrangeRoom ssrr on ssrr.WTSASessionSubrangeID = ssr.WTSASessionSubrangeID
您正在将WTSA_会话子范围加入WTSA_会话子范围。WTSA_SessionSubrange中可能没有行

连接应如下所示:

WTSA_SessionRange sr
left outer join
(SELECT WTSASessionRangeID, columns I need
FROM
    WTSA_SessionSubrange ssr
    left outer join
    WTSA_SessionSubrangeRoom ssrr on ssrr.WTSASessionSubrangeID = ssr.WTSASessionSubrangeID
) foo on foo.WTSASessionRangeID = sr.WTSASessionRangeID
这就是连接顺序影响结果的原因,因为从声明的角度讲,它是一个不同的查询


您还需要更改
MO_流
WTSA_SessionRangeStream
连接。

连接顺序确实会对结果查询产生影响。这在以下文件的BOL中记录:

是两个或多个表的乘积的结果集。对于多个联接,请使用括号更改联接的自然顺序

您可以使用连接周围的括号来更改连接顺序(BOL确实在文档顶部的语法中显示了这一点,但很容易忽略)


这就是所谓的交叉行为。您也可以使用查询提示
选项(强制订单)
强制执行特定的连接订单,但这可能会导致所谓的“繁杂计划”,对于正在执行的查询来说,这可能不是最理想的。

感谢Eric的输入-我将做更多的调整,看看是否可以进一步降低成本。用于联接的所有列上都有索引。不幸的是,数据的性质意味着我必须使用外部联接进行此操作。“显然,SQLServer2005优化器比SQLServer2000优化器好得多。”这是一种轻描淡写的说法。SQLServer2000查询优化器不仅很弱(在我看来,它与2000版的其他部分一样),而且常常看起来过于优化,做出错误的假设,从而导致非常不正确的数据。一个视图的联合,或者一个包含联合的视图,或者一个包含视图(!)的视图,一旦你超过了某个复杂度阈值,就会触发这种愚蠢行为。我一直无法准确地指出问题的本质,但我经常被它所困扰。这取决于表格的结构。你对T1-T2,T2-T3的情况是正确的。在我的情况下是T1-T2,T1-T3。@geofftnz:看我的答案。你不是T1-T2,T1-T3什么情况会导致这两种情况