这绝对不是一个规范的答案,但是我注意到,对于SQL Fiddle中显示的嵌套循环查询计划,可以通过使用提示将计划从Query 2应用于Query 1,USE PLAN
但是尝试反向操作会失败
查询处理器无法生成查询计划,因为USE PLAN提示包含无法验证为合法查询的计划。删除或替换USE PLAN提示。为了最大可能地成功执行计划,请验证USE PLAN提示中提供的计划是否是SQL Server为同一查询自动生成的计划。
禁用优化器转换规则 ReorderLOJN
也会阻止先前成功的计划提示也成功。
对大量数据进行的实验表明,SQL Server当然也可以转换(A LOJ B) LOJ C
为A LOJ (B LOJ C)
自然数据,但是我没有看到任何证据表明事实相反。
第一个查询的性能比第二个查询好的一个非常人为的情况是
DROP TABLE MyGrandChild , MyChild, MyParent
CREATE TABLE MyParent
(Id int)
CREATE TABLE MyChild
(Id int PRIMARY KEY
,ParentId int,
Filler char(8000) NULL)
CREATE TABLE MyGrandChild
(Id int
,ParentId int)
INSERT INTO MyChild
(Id, ParentId)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY @@SPID),
ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values v1, master..spt_values
INSERT INTO MyGrandChild
(Id, ParentId)
OUTPUT INSERTED.Id INTO MyParent
SELECT TOP (3000) Id, Id AS ParentId
FROM MyChild
ORDER BY Id
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
SELECT gc.Id AS gcId,
gc.ParentId AS gcpId,
c.Id AS cId,
c.ParentId AS cpId,
p.Id AS pId
FROM MyGrandChild AS gc
LEFT OUTER JOIN MyChild AS c
ON c.[Id] = gc.[ParentId]
LEFT OUTER JOIN MyParent AS p
ON p.[Id] = c.[ParentId]
SELECT gc.Id AS gcId,
gc.ParentId AS gcpId,
c.Id AS cId,
c.ParentId AS cpId,
p.Id AS pId
FROM MyGrandChild AS gc
LEFT OUTER JOIN( MyChild AS c
LEFT OUTER JOIN MyParent AS p
ON p.[Id] = c.[ParentId])
ON c.[Id] = gc.[ParentId]
给出计划
对我来说,查询1的经过时间为108毫秒,而查询2的经过时间为1,163毫秒。
查询1
Table 'Worktable'. Scan count 0, logical reads 0
Table 'MyChild'. Scan count 0, logical reads 9196
Table 'MyGrandChild'. Scan count 1, logical reads 7
Table 'MyParent'. Scan count 1, logical reads 5
查询2
Table 'MyParent'. Scan count 1, logical reads 15000
Table 'MyChild'. Scan count 0, logical reads 9000
Table 'MyGrandChild'. Scan count 1, logical reads 7
因此,可以暂时假定第一种(“未嵌套的”)语法是潜在的好处,因为它允许考虑更多潜在的加入顺序,但是作为一般规则,我没有进行详尽的测试以对它有很大的信心。
完全有可能提出查询2性能更好的反例。尝试两者并查看执行计划。