Почему IsNull (LTrim (RTrim (Lower (null))), -1) - *?

Сегодня я тестировал что-то на рабочем месте и наткнулся на это

Случай 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  Я ожидал одинаковых результатов в обоих случаях. Любая причина?

Ответ 1

Без объявления типа данных 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, особенно когда сложные выражения затрудняют предсказать, какие правила оценки могут превзойти приоритет типа данных.

Ответ 2

В SQL Server существуют "типизированные NULL" и "нетипизированные NULL".

В первом случае набирается 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, и вы увидите, что int намного выше, чем varchar, поэтому все выражение становится int.

Ответ 3

Голый NULL передается в LOWER(), который ожидает персонажа. Это значение по умолчанию равно одному символу. Значение "-1" не подходит в этом поле, поэтому оно возвращает "*".

Вы можете получить тот же эффект:

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

Следующий код также вызывает проблему:

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

Обратите внимание, что COALESCE() не вызывает этой проблемы.

Я уверен, что это полностью документированное поведение.