为什么IsNull(LTrim(RTrim(Lower(null))), - 1)是*?

时间:2012-08-09 18:26:53

标签: sql sql-server-2008 tsql

今天我在工作场所测试了一些东西并遇到了这个问题

案例1:

Declare @a nvarchar(20)
Set @a = null
Select IsNull(LTrim(RTrim(Lower(@a))), -1)

案例2:

Select IsNull(LTrim(RTrim(Lower(null))), -1)

案例1中的结果为-1,但案例2中为*  在这两种情况下我都期待相同的结果。有什么原因吗?

3 个答案:

答案 0 :(得分:13)

如果没有数据类型的声明,则在这种情况下,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显式提供的“值”做任何假设,特别是当复杂表达式难以预测哪些评估规则可能胜过数据类型优先时。

答案 1 :(得分:1)

在SQL Server中,有“typed NULLs”和“untyped NULLs”。

在第一种情况下,输入NULL - 它知道NULL是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 Data Type Precedence,您会发现int远高于varchar,因此整个表达式变为int

答案 2 :(得分:0)

将裸NULL传递给LOWER(),它需要一个字符。这被默认为一个字符宽。值“-1”不适合此字段,因此返回“*”。

您可以通过以下方式获得相同的效果:

select isnull(CAST(NULL as varchar(1)), -1)

以下代码也会导致问题:

declare @val varchar;
set @val = -1
select @val

请注意,COALESCE()不会导致此问题。

我很确定这是完全记录的行为。