sql-server 为什么查询计划中的更改似乎会突然中断查询

5f0d552i  于 2022-10-31  发布在  其他
关注(0)|答案(1)|浏览(155)

我试图更好地了解最近在生产中发生的一个问题。
一直在正常运行的查询突然停止,并显示以下错误:
传递给LEFT或SUBSTRING函数的长度参数无效。
查询(稍微简化):

SELECT t.client
FROM acuhistr t
    ,acuheader h
    ,acrclient c
WHERE t.ext_inv_ref IN (
        SELECT substring(ext_inv_ref, 1, len(ext_inv_ref) - 3)
        FROM acutrans
        WHERE ext_inv_ref LIKE '%ROT'
            AND client = t.client
        )
    AND t.apar_id = h.apar_id
    AND c.client = t.client
    AND h.client = c.leg_act_cli

我可以通过向substring()传递一个负长度参数来重现此错误消息:

SELECT substring('test', 1, -1)

传递给子字符串函数的长度参数无效。
acutrans表确实包含ext_inv_ref为空的行,但这些行应该已被LIKE条件筛选掉。如果我单独运行子查询,它将成功运行。
我还尝试在开发环境中运行完整的查询,它工作正常。
比较prod和dev之间的查询计划,似乎是prod查询计划在应用LIKE '%ROT'条件之前执行SUBSTRING(),这导致查询失败。
Image comparing query plans between prod and dev
我将新的查询计划确定为查询停止工作的原因,对吗?如果是,SQL Server为什么生成中断查询的计划?
SQL Server版本为2019

vohkndzv

vohkndzv1#

因为过滤器和表达式可能会以不同的顺序求值(例如,在过滤掉中断的值之前或之后)。更简单地想一想,假设你是一个理发师,大厅里挤满了顾客:

  • 从每位顾客身上剪下一寸头发
  • 过滤掉秃头的家伙

  • 过滤掉秃头的家伙
  • 从剩下的顾客身上剪下一英寸的头发

下面是SQL Server中一个更简单的示例:

  • 从字符串中获取月份名称
  • 筛选出不是有效日期的字符串

  • 筛选出不是有效日期的字符串
  • 从剩余的有效日期字符串中获取月份名称

在SQL Server中解决此问题的一种方法是使用CASE表达式仅计算不中断表达式的行(或者,在本例中,更改表达式使其不中断):

substring(ext_inv_ref, 1, len(ext_inv_ref) - CASE 
  WHEN len(ext_inv_ref) >= 3 THEN 3 ELSE 0 END)

用简单的英语来说,这只是说删除最右边的3个字符,但 * 只有 * 如果字符串已经至少有3个字符长。
或者you can use COALESCE/NULLIF或其他各种方法,具体取决于输入小于3个字符时是要空字符串还是NULL。
其他一些问题,以获得更多见解:

相关问题