函数使用空大小写操作挂起


9

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

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时函数挂起?

SQL小提琴

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

您能否发布更多逻辑信息?您在那里所拥有的应该不会造成问题。
肯尼斯·费舍尔

3
如果更换CASECOALESCE(@dateEnd,@dateStart),也仍然会出现问题?
ypercubeᵀᴹ

2
和一起ISNULL()
ypercubeᵀᴹ

3
是忙还是在等待什么?虽然是“挂起”,但SELECT task_state FROM sys.dm_os_tasks WHERE session_id = x 显示了什么?如果花费大量时间不处于该RUNNING状态,则该会话进入哪种等待类型sys.dm_os_waiting_tasks
马丁·史密斯

1
@ypercube没有任何改善COALESCEISNULL固定它。
柯米特(Kermit)2013年

Answers:


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) 

计划的该部分如下所示

在此处输入图片说明

您修改后的查询 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值的注释不符。


“在该表的“日期”列中是否有很高比例的NULL值?” NULL在那些表中都没有日期值。
柯米特(Kermit)2013年

@FreshPrinceOfSO-真可惜。我仍然不知道为什么两个估算值之间会有如此大的差异。在测试中,我做了位图过滤器,其他谓词似乎并没有改变基数估计,也许在这里。
马丁·史密斯

@FreshPrinceOfSO-尽管如果您希望编写统计数据的脚本,我可以尝试将其弄清楚。
马丁·史密斯

我正在使用2008R2;当我进入“选择架构”时dbo未列出。只是我不使用的其他模式。
柯米特(Kermit)2013年

4

好像数据类型有问题。ISNULL解决了该问题(感谢ypercube)。经过研究,COALESCE它等同CASE我正在使用的语句:

CASE
   WHEN (expression1 IS NOT NULL) THEN expression1
   WHEN (expression2 IS NOT NULL) THEN expression2
   ...
   ELSE expressionN
END

保罗·怀特(Paul White)解释说:

COALESCE( expression [ ,...n ] ) 返回具有最高数据类型优先级的表达式的数据类型。

ISNULL(check_expression, replacement_value) 返回与check_expression相同的类型。

为避免出现任何数据类型问题,似乎ISNULL仅用于处理两个表达式的函数就是合适的。

XML计划摘录

使用XML计划CASE,表达式2为NULL

SELECT * FROM theFunction ('2013-06-01', NULL)
<ScalarOperator ScalarString="CASE WHEN (1) THEN '2013-06-01' ELSE NULL END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Const ConstValue="(1)"/>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="'2013-06-01'"/>
      </ScalarOperator>
    </Then>
    <Else>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

使用的XML计划CASE,表达式2是一个日期:

SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
<ScalarOperator ScalarString="CASE WHEN [Expr1035]=(0) THEN NULL ELSE [Expr1036] END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Compare CompareOp="EQ">
          <ScalarOperator>
            <Identifier>
              <ColumnReference Column="Expr1035"/>
            </Identifier>
          </ScalarOperator>
          <ScalarOperator>
            <Const ConstValue="(0)"/>
          </ScalarOperator>
        </Compare>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
      </Then>
    <Else>
      <ScalarOperator>
        <Identifier>
          <ColumnReference Column="Expr1036"/>
        </Identifier>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

使用XML计划ISNULL,表达式2为NULL

SELECT * FROM theFunction ('2013-06-01', NULL)
<ScalarOperator ScalarString="CASE WHEN [Expr1035]=(0) THEN NULL ELSE [Expr1036] END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Compare CompareOp="EQ">
          <ScalarOperator>
            <Identifier>
              <ColumnReference Column="Expr1035"/>
            </Identifier>
          </ScalarOperator>
          <ScalarOperator>
            <Const ConstValue="(0)"/>
          </ScalarOperator>
        </Compare>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
    </Then>
    <Else>
      <ScalarOperator>
        <Identifier>
          <ColumnReference Column="Expr1036"/>
        </Identifier>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

使用的XML计划ISNULL,表达式2是一个日期:

SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
<ScalarOperator ScalarString="CASE WHEN [Expr1035]=(0) THEN NULL ELSE [Expr1036] END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Compare CompareOp="EQ">
          <ScalarOperator>
            <Identifier>
              <ColumnReference Column="Expr1035"/>
            </Identifier>
          </ScalarOperator>
          <ScalarOperator>
            <Const ConstValue="(0)"/>
          </ScalarOperator>
        </Compare>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
    </Then>
    <Else>
      <ScalarOperator>
        <Identifier>
          <ColumnReference Column="Expr1036"/>
        </Identifier>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

但这并不能解释为什么它可以正常工作SELECT * FROM theFunction ('2013-06-01', '2013-06-01')。表达式数据类型仍然相同。而且两个参数都是date数据类型。您可以查看执行计划吗?
马丁·史密斯

@MartinSmith 是返回结果的查询的计划。当第二个表达式为时,我没有计划NULL
柯米特(Kermit)2013年

将表达式中的表达式CASE也没有效果,查询仍然挂起。
柯米特(Kermit)2013年

2
为什么第二种情况没有计划呢?仅仅是因为查询永远不会结束吗?如果可以,您可以得到一个估计的计划吗?想知道不同的表达式是否会更改基数估计,您最终会得出不同的计划。
马丁·史密斯

3
ISNULL计划看起来更好了。它在的ColleagueList上有一个简单的相等谓词,[Date]='2013-06-01'而在的上 CASE有一个谓词[Date]>='2013-06-01' AND [Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END AND PROBE([Bitmap1067],[Date])。今后的说出来估计行加入是28393的ISNULL版本,但在低得多79.8CASE这些效应在计划以后加入的选择版本。不知道为什么会有这样的差异。
马丁·史密斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.