SQL Server中ISNULL/NULLIF组合的异常行为问询
咱们一步步拆解你遇到的异常,就能明白为什么结果会被截断:
RTRIM(NULL)的类型推导
当你直接调用RTRIM(NULL)而没有指定变量类型时,SQL Server会默认把这个表达式的返回类型推导为varchar(1)——因为NULL本身没有类型,RTRIM函数处理无类型NULL时,会返回最小长度的varchar类型,也就是varchar(1)的NULL值。NULLIF(RTrim(NULL), ' ')的返回类型NULLIF要求两个参数类型兼容,这里' '是varchar(1),和前面的varchar(1)匹配,所以这个表达式返回的是varchar(1)类型的NULL。ISNULL的强制类型转换逻辑(关键)
ISNULL函数的核心规则是:将第二个参数隐式转换为第一个参数的数据类型。第一个参数是varchar(1),所以第二个参数'**'(原本是varchar(2))会被截断成varchar(1)长度的字符串,也就是'*'——这就是你得到单个星号的原因。
再看那两个能正常工作的场景,为啥它们没问题:
场景1:用sql_variant变量存储RTRIM(NULL)
当你把RTRIM(NULL)存入sql_variant类型的变量@a时,SQL Server会将变量的类型推导为varchar(8000)(这是varchar类型的默认最大长度,未指定具体长度时的默认值)。后续NULLIF(@a, ' ')返回的是varchar(8000)类型的NULL,ISNULL把'**'转换为varchar(8000)时完全不会截断,因此得到正确的'**'。
场景2:用char(2)变量
当你声明@a char(2)并赋值为NULL时,RTRIM(@a)返回的是char(2)类型的NULL。接下来NULLIF(RTrim(@a), ' ')会把' '(varchar(1))隐式转换为char(2)(即' '),但因为第一个参数是NULL,所以NULLIF还是返回char(2)类型的NULL。最后ISNULL把'**'转换为char(2),长度刚好匹配,不会截断,因此结果正确。
总结来说:这个异常是SQL Server对无类型NULL的表达式类型推导规则,加上ISNULL函数的强制类型转换逻辑共同导致的。直接嵌套函数时,中间结果的类型被推导为最小长度的varchar,触发了截断;而用变量存储时,变量的显式类型(或sql_variant的默认长度)改变了中间结果的类型长度,避免了截断。
内容的提问来源于stack exchange,提问作者Akshay

