函数因空大小写操作而挂起

Ker*_*mit 9 sql-server sql-server-2008-r2

我创建了一个接受开始和结束日期的函数,结束日期是可选的。然后我CASE在过滤器中写了一个,如果没有通过结束日期,则使用开始日期。

CASE WHEN @dateEnd IS NULL
    THEN @dateStart
    ELSE @dateEnd
END
Run Code Online (Sandbox Code Playgroud)

当我为最近一个月的数据调用函数时:

SELECT * FROM theFunction ('2013-06-01', NULL)
Run Code Online (Sandbox Code Playgroud)

...查询挂起。如果我指定结束日期:

SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
Run Code Online (Sandbox Code Playgroud)

... 结果正常返回。我从函数中取出代码并在查询窗口中正常运行。我也不能复制小提琴的问题。像这样的查询:

SELECT * FROM theFunction ('2013-04-01', '2013-06-01')
Run Code Online (Sandbox Code Playgroud)

...也工作正常。

查询(下面)中是否有任何内容可能导致函数NULL在结束日期传递时挂起?

SQL小提琴

  • 执行计划SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
  • 预计计划SELECT * FROM theFunction ('2013-06-01', NULL)

Mar*_*ith 7

您的初始查询的一部分如下。

  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) 
Run Code Online (Sandbox Code Playgroud)

计划的那部分如下所示

在此处输入图片说明

您修改后的查询 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'
Run Code Online (Sandbox Code Playgroud)

由于有一个 equi join 谓词b.[Date] = a.d,计划也显示了一个相等谓词b.[Date] = '2013-06-01'。因此,28,393行的基数估计可能非常准确。

对于CASE/COALESCE版本 when@dateStart@dateEndis 相同的值,那么它会将 OK 简化为相同的相等表达式并给出相同的计划,但是 when@dateStart = '2013-06-01'@dateEnd IS NULLit only as far as

a.[d]>='2013-06-01' AND a.[Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END
Run Code Online (Sandbox Code Playgroud)

它也适用于 的隐含谓词ColleagueList。这次估计的行数是79.8行。

下一个加入是

   LEFT JOIN colleagueTime
     ON colleagueTime.TC_DATE = colleagueList.Date
        AND colleagueTime.ASSOC_ID = CAST(colleagueList.ID AS VARCHAR(10)) 
Run Code Online (Sandbox Code Playgroud)

colleagueTime是一个3,249,590行表,它(再次)显然是一个没有有用索引的堆。

这种估计差异会影响所使用的连接选择。该ISNULL计划选择只扫描表一次的散列连接。该COALESCE计划选择了一个嵌套循环连接,并估计它仍然只需要扫描一次表并能够假脱机结果并重放它 78 次。即它估计相关参数不会改变。

由于嵌套循环计划在两小时后仍在进行,因此单次扫描的假设colleagueTime似乎非常不准确。

至于为什么两个连接之间的估计行数如此之低,我不确定无法看到表上的统计信息。在我的测试中,我设法使估计的行数倾斜的唯一方法是添加NULL行负载(这减少了估计的行数,即使返回的实际行数保持不变)。

COALESCE我的测试数据计划中的估计行数为

number of rows matching >= condition * 30% * (proportion of rows in the table not null)
Run Code Online (Sandbox Code Playgroud)

或者在 SQL 中

SELECT 1E0 * COUNT([Date]) / COUNT(*) * ( COUNT(CASE
                                                  WHEN [Date] >= '2013-06-01' THEN 1
                                                END) * 0.30 )
FROM   [dbo].[colleagueList] 
Run Code Online (Sandbox Code Playgroud)

但这与您对该列没有NULL值的评论不符。