今天我在工作场所测试一些东西,遇到了这个
情况1:
Declare @a nvarchar(20)
Set @a = null
Select IsNull(LTrim(RTrim(Lower(@a))), -1)
案例二:
Select IsNull(LTrim(RTrim(Lower(null))), -1)
案例 1 的结果是,-1
但*
在案例 2 中,我期望两种情况下的结果相同。任何原因?
今天我在工作场所测试一些东西,遇到了这个
情况1:
Declare @a nvarchar(20)
Set @a = null
Select IsNull(LTrim(RTrim(Lower(@a))), -1)
案例二:
Select IsNull(LTrim(RTrim(Lower(null))), -1)
案例 1 的结果是,-1
但*
在案例 2 中,我期望两种情况下的结果相同。任何原因?
在没有数据类型声明的情况下,这种情况下的 null 被声明为 varchar(1)。您可以通过将结果选择到 #temp 表中来观察这一点:
Select IsNull(LTrim(RTrim(Lower(null))), -1) as x INTO #x;
EXEC tempdb..sp_help '#x';
在您将看到的结果中:
Column_name Type Length
----------- ------- ------
x varchar 1
由于 -1 无法放入 varchar(1),因此您将得到 * 作为输出。这类似于:
SELECT CONVERT(VARCHAR(1), -1);
如果你想折叠成一个字符串,那么我建议将整数括在单引号中,这样就不会因整数 <-> 字符串转换而导致的混淆:
SELECT CONVERT(VARCHAR(1), '-1'); -- yields "-"
SELECT CONVERT(VARCHAR(30), '-1'); -- yields "-1"
我不会对 SQL Server 将如何处理显式提供的“值”做任何假设null
,尤其是当复杂的表达式难以预测哪些评估规则可能胜过数据类型优先级时。
在 SQL Server 中,有“类型化 NULL”和“非类型化 NULL”。
在第一种情况下,NULL 是有类型的——它知道 NULL 是 a varchar(20)
,因此当您的函数包装内部值时,该数据类型会在整个表达式中传播。
在第二种情况下,NULL 是无类型的,因此它必须从周围的表达式中推断出 NULL 的类型。该IsNull
函数计算第一个操作数的数据类型并将其应用于整个表达式,因此 NULL 默认为varchar(1)
:
PRINT sql_variant_property(IsNull(LTrim(NULL), -1), 'BaseType'); -- varchar
PRINT sql_variant_property(IsNull(LTrim(NULL), -1), 'MaxLength'); -- 1
另一个复杂之处是它IsNull
没有以与它相同的方式进行类型提升Coalesce
(尽管 Coalesce 由于不是函数而有其自身的问题 - 它被扩展为 CASE 表达式,有时由于重复表达式评估而导致意外的副作用)。看:
SELECT Coalesce(LTrim(NULL), -1);
这导致-1
数据类型int
!
查看Sql Server 数据类型优先级,您会发现它int
远高于varchar
,因此整个表达式变为int
.
裸露的 NULL 被传递给 LOWER(),它需要一个字符。这默认为一个字符宽。值“-1”不适合此字段,因此返回“*”。
您可以通过以下方式获得相同的效果:
select isnull(CAST(NULL as varchar(1)), -1)
以下代码也会导致问题:
declare @val varchar;
set @val = -1
select @val
请注意,COALESCE() 不会导致此问题。
我很确定这是完全记录在案的行为。