传递给LEFT或SUBSTRING函数UNION ALL的长度参数无效

时间:2017-07-14 16:53:56

标签: tsql sql-server-2012

我有两个查询在SUBSTRING语句中使用CASE函数,如下所示:

CASE
    WHEN Answer.ChoiceTitle = 'Neither Likely or Unlikely'
        THEN 'Neither Likely nor Unlikely'
    WHEN Answer.ChoiceTitle LIKE '[1-5]%'
        THEN SUBSTRING(Answer.ChoiceTitle, 3, LEN(Answer.ChoiceTitle) - 2)
    ELSE Answer.ChoiceTitle
END AS Recommendation

当单独运行时,两个查询都运行得很好但是当我尝试将两个结果集与UNION ALL组合时,我收到错误消息:

  

传递给LEFT或SUBSTRING函数的长度参数无效

在尝试弄清楚为什么会出现此错误的同时,我将以下内容添加到每个语句中,UNION ALL现在完全正常。

MIN(LEN(Answer.ChoiceTitle)) OVER() AS MinLength

为什么我会收到此错误?

执行计划

计划执行计划UNION ALL - https://www.brentozar.com/pastetheplan/?id=rksFnuLS-

第一个陈述的实际执行计划 - https://www.brentozar.com/pastetheplan/?id=r1Z-pO8HW

第二项陈述的实际执行计划 - https://www.brentozar.com/pastetheplan/?id=rkCTh_IBb

1 个答案:

答案 0 :(得分:1)

这很可能导致您的错误:LEN(Answer.ChoiceTitle) - 2

当评估值小于0时,它将引发错误。

请改为尝试:

CASE
    WHEN Answer.ChoiceTitle = 'Neither Likely or Unlikely'
        THEN 'Neither Likely nor Unlikely'
    WHEN Answer.ChoiceTitle LIKE '[1-5]%' and LEN(Answer.ChoiceTitle) > 2
        THEN SUBSTRING(Answer.ChoiceTitle, 3, LEN(Answer.ChoiceTitle) - 2)
    ELSE Answer.ChoiceTitle
END AS Recommendation

由于你刚刚摆脱了前两个字符,你可以使用stuff()代替:

CASE
    WHEN Answer.ChoiceTitle = 'Neither Likely or Unlikely'
        THEN 'Neither Likely nor Unlikely'
    WHEN Answer.ChoiceTitle LIKE '[1-5]%'
        THEN stuff(Answer.ChoiceTitle,1,2,'')
    ELSE Answer.ChoiceTitle
END AS Recommendation

如果长度小于3,这将给你一个空字符串,否则它将删除Answer.ChoiceTitle的前两个字符。

至于为什么合并后的union all查询会在其他人单独运行时抛出错误:

我在执行计划中看到了这种差异:

Hash Match > (Question & Survey nested loop) & (Compute Scalar > Answer) {Bottom right of execution plan without error}
vs
Hash Match > (Bitmap > Parallelism > Question) & (Compute Scalar > Answer) {Bottom right of execution plan with error}

嵌套循环版本可能会过滤在散列匹配之前导致错误的行,从而避免错误。

使用option (maxdop 1)来防止并行可能会避免错误(已确认)在当前正在抛出它的查询上 这只是在为答案表中的行计算标量函数时,在过滤掉要运行表达式的行之前或之后。

union all版本的成本较高,并且它超出了并行性的成本阈值,这就是为什么在不并行的情况下单独运行时不会看到相同的错误(特别是并行)以同样的方式)单独运行时成本较低。

所以基本上并行计划比其他计划更快地运行substring(),然后过滤掉抛出错误的行。