为啥 T-SQL ISNULL() 截断字符串而 COALESCE 不截断?

Posted

技术标签:

【中文标题】为啥 T-SQL ISNULL() 截断字符串而 COALESCE 不截断?【英文标题】:Why is T-SQL ISNULL() truncating the string and COALESCE is not?为什么 T-SQL ISNULL() 截断字符串而 COALESCE 不截断? 【发布时间】:2013-09-23 23:33:33 【问题描述】:

鉴于以下情况:

SELECT ISNULL('XY' + NULL, 'ABCDEFGHIJ') -- Outputs ABC (Why?)
SELECT COALESCE('XY' + NULL, 'ABCDEFGHIJ') -- Outputs ABCDEFGHIJ

为什么这些语句返回不同的结果?

【问题讨论】:

【参考方案1】:

根据微软documentation,功能:

ISNULL(check_expression, replacement_value)

replacement_value 必须是可隐式转换为check_expression 类型的类型。请注意'xy'+NULL 的类型是VARCHAR(3)。因此,您的字符串 'ABCDEFGHIJ' 被强制转换为 VARCHAR(3) 并因此被修剪。

为什么不是VARCHAR(2) 听起来很奇怪,但事实就是这样——比'xy' 长一个字符。您可以使用这个 SQLFiddle 并亲自查看'xy'+NULL 的类型与表达式CASE WHEN 1=2 THEN 'XYZ' ELSE NULL END 相同,即NULL 但隐式兼容VARCHAR(3)

对于表达式'xy'+NULL,感知长度似乎可以计算为'xy' 字符串长度(2)加上每添加一个NULL 1。例如,'xy'+NULL+NULL 的类型是 VARCHAR(4)'xy'+NULL+NULL+NULL 的类型是 VARCHAR(5) 等等 - 看看这个 SQLFiddle。这非常奇怪,但这就是 MS SQL Server 2008 和 2012 的工作方式。

【讨论】:

很好的解释,虽然我不相信 varchar(3) 而不是 varchar(2) - 我在任何地方都找不到它的文档。 我必须增加这里的信息来解释更多关于 COALESCE 行为而不是 ISNULL 行为的信息,来自 Microsoft documentation:COALESCE 表达式是 CASE 表达式的语法快捷方式。也就是说,查询优化器将代码 COALESCE(expression1,...n) 重写为以下 CASE 表达式: CASE WHEN (expression1 IS NOT NULL) THEN expression1 WHEN (expression2 IS NOT NULL) THEN expression2 ... ELSE expressionN END -> case 返回类型由最高的 expr 优先级给出。 这解释了COALESCE 的行为,但没有说明为什么'xy'+NULL 的类型是VARCHAR(3)。这一定与 SQL Server 内部有关。 我不确定我是否得到了你的样品证明。第二个查询是一个 varchar(3) 因为 'XYZ' 而不是因为 NULL 的 CASE,对吧?所以我猜剩下的疑问是关于 'X'+NULL 变成 varchar(2) 而不是 varchar(1)。那么,在这种情况下,NULL 是否被视为单个字符?使用 ISNULL 连接字符串时是否需要担心溢出/截断? 是的,NULL 增加了 1 个字符。这很奇怪,但这里有确凿的证据:sqlfiddle.com/#!3/d41d8/20994【参考方案2】:

您可以在这里查看所有差异,非常清楚

MSDN:http://msdn.microsoft.com/en-us/library/ms190349.aspx

MSDN 博客:http://blogs.msdn.com/b/sqltips/archive/2008/06/26/differences-between-isnull-and-coalesce.aspx

【讨论】:

+1 为博客文章,我在第一次研究时没有注意到它。很好的资源。【参考方案3】:

ISNULL() 将替换值转换为检查表达式的类型。在这种情况下,检查表达式的类型是CHAR(2),因此转换替换值会截断它(你确定你得到的是ABC 而不仅仅是AB?)。

来自微软documentation:

如果replacement_valuecheck_expression 长,replacement_value 可以被截断。

【讨论】:

以上是关于为啥 T-SQL ISNULL() 截断字符串而 COALESCE 不截断?的主要内容,如果未能解决你的问题,请参考以下文章

T-SQL字符串相加之后被截断的那点事

T-SQL和MySQL的一些常用语法的区别

将 T-SQL ISNULL 函数逻辑复制到 SparkSQL

IsNull、IsEmpty、=Empty 和空字符串(即“”)有啥不同,为啥我可以使用变体

长整数在插入较短的列时进行转换,而不是截断。为啥?公式是啥?

为啥Firebird在除法时会截断小数位?