INNER / RIGHT / LEFT JOIN如何比FULL JOIN慢14倍?

时间:2013-03-07 23:23:11

标签: sql-server sql-server-2012

我有一个查询,使用FULL JOIN需要2.5秒,使用INNER,RIGHT或LEFT JOIN需要40秒。

这是查询。子查询(完成两次)只需要1.3秒。

SELECT T1.[time], T1.Total, T1.rn, T2.[time], T2.Total, T2.rn
FROM
(
select [time], MAX(ComputedValue) as Total, row_number() over (order by [time]) as rn
FROM
(
    select SUBSTRING(CONVERT(CHAR(10), IntervalStartTime, 108), 0, 6) as [time], ComputedValue
    from LoadTestTransactionSample
    where LoadTestRunId=285
    and CounterName='Total Transactions' 
    and TransactionName='Export'
) foo
group by [time]
) T1
_____ JOIN
(
select [time], MAX(ComputedValue) as Total, row_number() over (order by [time]) as rn
FROM
(
    select SUBSTRING(CONVERT(CHAR(10), IntervalStartTime, 108), 0, 6) as [time], ComputedValue
    from LoadTestTransactionSample
    where LoadTestRunId=285
    and CounterName='Total Transactions' 
    and TransactionName='Export'
) foo
group by [time]
) T2
ON T1.rn = T2.rn - 1

select SUBSTRING位只是从DateTime中获取HH:MM字符串。 LoadTestTransactionSample实际上是一个连接8个表的VIEW。 (仅供参考,数据库是Visual Studio加载测试结果存储区)。以下是其(相关)专栏:

LoadTestRunId INT NOT NULL
CounterName NVARCHAR(255) NOT NULL
TransactionName NVARCHAR(64) NOT NULL
IntervalStartTime DATETIME NOT NULL
IntervalEndTime DATETIME NOT NULL
ComputedValue REAL

FULL JOIN会返回一个额外的不需要的行,所以我需要做一个正确的JOIN才能得到正确的答案。

我不是在寻找解决方案(我有一个:将子查询预取到表变量中使用SQL Server 2012分析函数'LAG',感谢@ a1ex07),只是对可能导致这些连接类型之间性能差异的原因的理解


修改 这是slow right join query planfast full join query plan。它们太大了,无法发布截图。

编辑2: 实际上,查询计划的正确加入率为45%,加入加权值为55%,结果是完全不准确(实际上最终会低于99%/ 1%)。我想这意味着我必须依赖实际的执行统计数据。

编辑3: RIGHT JOIN的统计数据:

(40 row(s) affected)
Table 'LoadTestPerformanceCounterCategory'. Scan count 0, logical reads 37556, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestPerformanceCounter'. Scan count 0, logical reads 176464, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestScenario'. Scan count 0, logical reads 176464, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestCase'. Scan count 0, logical reads 176464, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'WebLoadTestTransaction'. Scan count 0, logical reads 13411100, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestPerformanceCounterInstance'. Scan count 0, logical reads 36563718, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestPerformanceCounterSample'. Scan count 19721, logical reads 269657, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestRunInterval'. Scan count 41, logical reads 205, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
    CPU time = 36754 ms,  elapsed time = 36763 ms.

快速加入的统计数据:

(41 row(s) affected)
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestPerformanceCounterCategory'. Scan count 0, logical reads 1832, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestPerformanceCounter'. Scan count 0, logical reads 8608, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestScenario'. Scan count 0, logical reads 8608, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestCase'. Scan count 0, logical reads 8608, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'WebLoadTestTransaction'. Scan count 0, logical reads 654200, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestPerformanceCounterInstance'. Scan count 0, logical reads 1783596, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestPerformanceCounterSample'. Scan count 962, logical reads 13154, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LoadTestRunInterval'. Scan count 2, logical reads 10, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
    CPU time = 1950 ms,  elapsed time = 1944 ms.

RIGHT JOIN正在进行比FULL JOIN更多的读取和更多扫描,尽管有一个明显相似的查询计划。

工作表(在FULL JOIN中)提示?这是临时表吗?

这似乎表明查询优化器已损坏吗?

2 个答案:

答案 0 :(得分:2)

好的,事实证明答案是:糟糕的数据库统计数据。很坏。如在,从未更新。

exec sp_updatestats; FTW。

[羞愧地隐藏]

答案 1 :(得分:1)

这是类似查询的执行计划。表非常小。

Execution Plan

查询1

  • 使用INNER JOIN
  • 执行计划看起来更小。
  • 但是,占总时间的62%。

查询2

  • 使用FULL JOIN
  • 执行计划看起来很大。
  • 但是,占总时间的38%。

原因: 在我的案例中INNER JOIN正在使用HASH MATCH。而且FULL JOIN正在使用NESTED LOOP。这由SQL优化器决定必须使用哪个物理连接(但是我们可以使用其他物理连接)。检查你的执行计划,它会有所帮助。