我创建了一个接受开始和结束日期的函数,结束日期是可选的。然后我在过滤器中写了一个CASE
如果没有传递结束日期则使用开始日期。
CASE WHEN @dateEnd IS NULL
THEN @dateStart
ELSE @dateEnd
END
当我为最近一个月的数据调用该函数时:
SELECT * FROM theFunction ('2013-06-01', NULL)
...查询挂起。如果我指定结束日期:
SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
...结果正常返回。我从函数中取出代码并在查询窗口中运行它。我也不能用小提琴复制这个问题。查询如:
SELECT * FROM theFunction ('2013-04-01', '2013-06-01')
...也可以正常工作。
NULL
在结束日期传递a 时,查询(下方)中是否有任何内容可能导致函数挂起?
您的初始查询的一部分如下。
该计划的该部分如下所示
您修改后的查询
BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)
具有相同的连接不同之处似乎在于
ISNULL
进一步简化,因此您可以获得更准确的基数统计信息进入下一个连接。这是一个内联表值函数,您使用文字值调用它,因此它可以执行类似的操作。由于有一个 equi join predicate
b.[Date] = a.d
,该计划还显示了一个 equal predicateb.[Date] = '2013-06-01'
。因此,28,393
行的基数估计可能非常准确。对于
CASE
/COALESCE
版本 when@dateStart
和@dateEnd
是相同的值然后它将 OK 简化为相同的相等表达式并给出相同的计划但是 when@dateStart = '2013-06-01'
and@dateEnd IS NULL
它只达到它也适用于 上的隐含谓词
ColleagueList
。这次估计的行数是79.8
rows。下一个加入是
colleagueTime
是一个3,249,590
行表,它(再次)显然是一个没有有用索引的堆。这种估计差异会影响所使用的连接选择。该
ISNULL
计划选择一个只扫描表一次的散列连接。该COALESCE
计划选择嵌套循环连接并估计它仍然只需要扫描一次表并能够假脱机结果并重放 78 次。即它估计相关参数不会改变。由于嵌套循环计划在两个小时后仍在运行,因此这种针对单次扫描的假设
colleagueTime
似乎非常不准确。至于为什么两个连接之间的估计行数要低得多,我不确定不能看到表上的统计信息。在我的测试中,我设法使估计行数偏斜的唯一方法是添加
NULL
行负载(这减少了估计行数,即使返回的实际行数保持不变)。计划中的估计行数
COALESCE
与我的测试数据的顺序是或者在 SQL 中
但这与您关于该列没有
NULL
值的评论不一致。似乎数据类型存在问题。
ISNULL
解决了这个问题(感谢ypercube)。经过一些研究,COALESCE
相当于我使用的CASE
语句:保罗怀特解释说:
为避免任何数据类型问题,似乎
ISNULL
只使用处理两个表达式的合适函数。XML 计划摘录
XML 计划使用
CASE
,表达式 2 是NULL
:XML plan using
CASE
,表达式 2 是一个日期:XML 计划使用
ISNULL
,表达式 2 是NULL
:XML plan using
ISNULL
,表达式 2 是一个日期: