初始查询的一部分如下。
FROM [dbo].[calendar] a
LEFT JOIN [dbo].[colleagueList] b
ON b.[Date] = a.d
WHERE DAY(a.[d]) = 1
AND a.[d] BETWEEN @dateStart AND COALESCE(@dateEnd,@dateStart)
计划的该部分如下所示
您修改后的查询 BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)
具有相同的联接
差异似乎是ISNULL
进一步简化了,因此您将获得更准确的基数统计信息进入下一个联接。这是一个内联表值函数,您正在用文字值调用它,因此它可以执行类似操作。
a.[d] BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)
a.[d] BETWEEN '2013-06-01' AND ISNULL(NULL,'2013-06-01')
a.[d] BETWEEN '2013-06-01' AND '2013-06-01'
a.[d] = '2013-06-01'
并且由于存在等分谓词,b.[Date] = a.d
该计划还显示了等分谓词b.[Date] = '2013-06-01'
。结果,28,393
行的基数估计可能非常准确。
对于CASE
/ COALESCE
版本,当@dateStart
和@dateEnd
是相同的值时,它将OK简化为相同的相等表达式,并给出相同的计划,但是,当@dateStart = '2013-06-01'
and @dateEnd IS NULL
仅达到
a.[d]>='2013-06-01' AND a.[Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END
它也用作隐含谓词ColleagueList
。这次的估计行数是79.8
行。
接下来的连接是
LEFT JOIN colleagueTime
ON colleagueTime.TC_DATE = colleagueList.Date
AND colleagueTime.ASSOC_ID = CAST(colleagueList.ID AS VARCHAR(10))
colleagueTime
是一个3,249,590
行表,(显然)显然是一个没有有用索引的堆。
估计的差异会影响所使用的联接选择。该ISNULL
计划选择仅扫描表一次的哈希联接。该COALESCE
计划选择了一个嵌套循环联接,并估计它仍将只需要扫描表一次并能够假脱机结果并将其重播78次。即它估计相关的参数将不会改变。
从两个小时后仍在执行嵌套循环计划这一事实来看,对单次扫描的假设colleagueTime
可能非常不准确。
至于为什么两个联接之间的估计行数如此之低的原因,我不确定是否无法查看表中的统计信息。在测试中,我设法使估计的行数偏斜的唯一方法是增加NULL
行的负载(即使返回的实际行数保持不变,这也会减少估计的行数)。
COALESCE
包含我的测试数据的计划中的估计行数约为
number of rows matching >= condition * 30% * (proportion of rows in the table not null)
或在SQL中
SELECT 1E0 * COUNT([Date]) / COUNT(*) * ( COUNT(CASE
WHEN [Date] >= '2013-06-01' THEN 1
END) * 0.30 )
FROM [dbo].[colleagueList]
但这与您认为该列没有NULL
值的注释不符。