简体   繁体   中英

Why IsNull(LTrim(RTrim(Lower(null))), -1) is *?

Today I was testing something at work place and came across this one

Case 1:

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

Case 2:

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

The result in case 1 is -1 but * in case 2 I was expecting same results in both cases. Any reason?

Without the declaration of data type, null in this case is declared as varchar(1). You can observe this by selecting the results into a #temp table:

Select IsNull(LTrim(RTrim(Lower(null))), -1) as x INTO #x;
EXEC tempdb..sp_help '#x';

Among the results you'll see:

Column_name   Type      Length
-----------   -------   ------
x             varchar   1

Since -1 can't fit in a varchar(1), you are getting * as output. This is similar to:

SELECT CONVERT(VARCHAR(1), -1);

If you want to collapse to a string, then I suggest enclosing the integer in single quotes so there is no confusion caused by integer <-> string conversions that aren't intended:

SELECT CONVERT(VARCHAR(1), '-1'); -- yields "-"
SELECT CONVERT(VARCHAR(30), '-1'); -- yields "-1"

I would not make any assumptions about how SQL Server will handle a "value" explicitly provided as null , especially when complex expressions make it difficult to predict which evaluation rules might trump data type precedence.

In SQL Server, there are "typed NULLs" and "untyped NULLs".

In the first case, the NULL is typed—it is aware that NULL is a varchar(20) and so as your functions wrap the inner value, that data type is propagated throughout the expression.

In the second case, the NULL is untyped, so it has to infer the NULL's type from the surrounding expressions. The IsNull function evaluates the data type of the first operand and applies that to the whole expression, and thus the NULL defaults to varchar(1) :

PRINT sql_variant_property(IsNull(LTrim(NULL), -1), 'BaseType'); -- varchar
PRINT sql_variant_property(IsNull(LTrim(NULL), -1), 'MaxLength'); -- 1

Another complication is that IsNull does not do type promotion in the same way that Coalesce does (though Coalesce has its own problems due to not being a function—it is expanded to a CASE expression, sometimes causing unexpected side-effects due to repeat expression evaluation). Look:

SELECT Coalesce(LTrim(NULL), -1);

This results in -1 with data type int !

Check out Sql Server Data Type Precedence and you'll see that int is much higher than varchar , so the whole expression becomes int .

The naked NULL is being passed to LOWER(), which expects a character. This is being defaulted to one character wide. The value "-1" doesn't fit in this field, so it is returning "*".

You can get the same effect with:

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

The following code also causes the problem:

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

Note that COALESCE() does not cause this problem.

I'm pretty sure this is fully documented behavior.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM