Предложение T-SQL CASE: как указать WHEN NULL


228

Я написал инструкцию T-SQL, похожую на эту (оригинал выглядит иначе, но я хочу привести простой пример):

SELECT first_name + 
    CASE last_name WHEN null THEN 'Max' ELSE 'Peter' END AS Name
FROM dbo.person

В этом операторе нет синтаксических ошибок, но предложение case всегда выбирает ELSE-часть - также, если last_name равно null. Но почему?

То, что я хочу сделать, это объединить first_name и last_name, но если last_name равно null, все имя становится пустым:

SELECT first_name +
   CASE last_name WHEN null THEN '' ELSE ' ' + last_name END AS Name 
FROM dbo.person

Вы знаете, где проблема?

Ответы:


370
CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END

4
Предложение Лютера COALESCE лучше, чем мой ответ. Это немного менее эффективно, но гораздо более элегантно.
— Марсело Кантос

Обработка этого с чем-то вроде этого может быть полезна, или что-то в этом роде: COALESCE (last_name, '') Оператор case, хотя и лаконичен, imho менее удобен, чем COALESCE в больших запросах. В итоге у вас тот же результат. Если вам нужно оптимизировать, проверьте планы выполнения, но я не заметил большой разницы.
— Энтони Мейсон,

1
COALESCEв основном внутренне переводится на CASE. Проблема с CASE заключается в том, что last_nameона оценивается дважды, и это может привести к другим побочным эффектам - см. Эту прекрасную статью . Чтобы решить, что спросили, я бы предпочел ISNULL(' '+ last_name, '')упомянуть в комментарии ниже.
— Miroxlav

41

Часть WHEN сравнивается с ==, но вы не можете сравнить с NULL. Пытаться

CASE WHEN last_name is NULL  THEN ... ELSE .. END

вместо этого или COALESCE:

COALESCE(' '+last_name,'')

('+ last_name имеет значение NULL, когда last_name равно NULL, поэтому в этом случае должно возвращаться' ')


Хорошо, спасибо за информацию с == в части КОГДА. Я этого не знал. С COALESCE это также работает нормально. Не думал, что есть так много возможностей сделать это.
— Meni

15

Есть много решений, но ни одно не объясняет, почему оригинальное утверждение не работает.

CASE last_name WHEN null THEN '' ELSE ' '+last_name

После когда происходит проверка на равенство, которая должна быть истинной или ложной.

Если одна или обе части сравнения имеют значение NULL, результатом сравнения будет UNKNOWN, что рассматривается как ложь в структуре дела. См .: https://www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/

Чтобы избежать этого, Coalesce - лучший способ.


11

По вашему запросу вы также можете сделать это:

SELECT first_name + ' ' + ISNULL(last_name, '') AS Name FROM dbo.person

2
Это добавляет избыточный пробел, когда last_name имеет значение null, чего и пытался избежать OP.
— Марсело Кантос

6
Лучше использовать, ISNULL(' '+ last_name, '')чтобы предотвратить избыточное пространство.
— Prutswonder

Да понял это после того как я написал. Думал, я бы оставил это, так как это решило ту часть, с которой у него были проблемы.
— Ян Джейкобс

7

Проблема в том, что нуль не считается равным самому себе, поэтому предложение никогда не совпадает.

Вам нужно явно проверить на ноль:

SELECT CASE WHEN last_name is NULL THEN first_name ELSE first_name + ' ' + last_name

5

пытаться:

SELECT first_name + ISNULL(' '+last_name, '') AS Name FROM dbo.person

Это добавляет пробел к фамилии, если она пуста, весь пробел + фамилия переходят в NULL, и вы получаете только имя, в противном случае вы получаете первые + пробел + фамилию.

это будет работать до тех пор, пока задано значение по умолчанию для объединения с пустыми строками:

SET CONCAT_NULL_YIELDS_NULL ON 

это не должно вызывать беспокойства, поскольку OFFв следующих версиях SQl Server этот режим исчезнет


4

Проблема в том, что NULL не считается равным чему-либо, даже не самому себе, но странная часть заключается в том, что он также не равен самому себе.

Рассмотрим следующие операторы (что, кстати, недопустимо в T-SQL для SQL Server, но допустимо в My-SQL, однако это то, что ANSI определяет для нуля, и может быть проверено даже в SQL Server с помощью операторов case и т. Д.)

SELECT NULL = NULL -- Results in NULL

SELECT NULL <> NULL -- Results in NULL

Таким образом, нет истинного / ложного ответа на вопрос, вместо этого ответ также является нулевым.

Это имеет много последствий, например, в

  1. СЛУЧАЙ заявление, в котором любое пустом значение будет всегда использовать ELSE положение , если не использовать явный вид КОГДА условие NULL ( НЕ условие )WHEN NULL
  2. Конкатенация строк, как
    SELECT a + NULL -- Results in NULL
  3. В предложении WHERE IN или WHERE NOT IN, как если вы хотите получить правильные результаты, убедитесь, что в коррелированном подзапросе отфильтрованы все нулевые значения.

Можно изменить это поведение в SQL Server, указав SET ANSI_NULLS OFF, однако это НЕ рекомендуется и не должно быть сделано, так как это может вызвать много проблем, просто из-за отклонения от стандарта.

(Как примечание, в My-SQL есть возможность использовать специальный оператор <=>для нулевого сравнения.)

Для сравнения, в общих языках программирования ноль рассматривается как обычное значение и равен самому себе, однако это значение NAN, которое также не равно самому себе, но, по крайней мере, возвращает «ложь» при сравнении его с собой, (и при проверке на не равные разные языки программирования имеют разные реализации).

Тем не менее, обратите внимание, что в базовых языках (то есть VB и т. Д.) Нет ключевого слова «null», и вместо этого используется ключевое слово «Nothing», которое нельзя использовать при прямом сравнении, а вместо этого нужно использовать «IS», как в SQL, однако он фактически равен самому себе (при использовании косвенных сравнений).


1
«Странная часть в том, что она также не равна себе». => Это не странно, учитывая, что нуль в SQL имеет значение «неизвестно». Что-то неизвестное, скорее всего, не то же самое, что нечто неизвестное.
— JDC


1

Когда вы расстроены, пытаясь это:

CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END

Попробуйте это вместо этого:

CASE LEN(ISNULL(last_Name,''))
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

LEN(ISNULL(last_Name,''))измеряет количество символов в этом столбце, которое будет равно нулю, независимо от того, пусто оно или NULL, поэтому WHEN 0 THENбудет иметь значение true и возвращать '', как и ожидалось.

Я надеюсь, что это полезная альтернатива.

Я включил этот тест для SQL Server 2008 и выше:

DECLARE @last_Name varchar(50) = NULL

SELECT 
CASE LEN(ISNULL(@last_Name,''))
WHEN 0 THEN '' 
ELSE 'A ' + @last_name
END AS newlastName

SET @last_Name = 'LastName'

SELECT 
CASE LEN(ISNULL(@last_Name,''))
WHEN 0 THEN '' 
ELSE 'A ' + @last_name
END AS newlastName

2
Вы уверены, что это работает? Большинство функций возвращают NULL, если дано значение NULL, и мои тесты подтверждают, что это также верно для LEN (), т. Е. LEN (NULL) возвращает NULL, а не 0.
— Джейсон Масгроув

Pokechu22 является правильным, и это исправленный сценарий: CASE LEN (ISNULL (last_Name, '')) КОГДА 0 THEN '' ELSE '' + last_name END AS newlastName
— Шеф-повар убивает

0

Джейсон поймал ошибку, так что это работает ...

Кто-нибудь может подтвердить другие версии платформы?
SQL Server:

SELECT
CASE LEN(ISNULL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

MySQL:

SELECT
CASE LENGTH(IFNULL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

Oracle:

SELECT
CASE LENGTH(NVL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

0

Вы можете использовать функцию IsNull

select 
    isnull(rtrim(ltrim([FirstName]))+' ','') +
    isnull(rtrim(ltrim([SecondName]))+' ','') +
    isnull(rtrim(ltrim([Surname]))+' ','') +
    isnull(rtrim(ltrim([SecondSurname])),'')
from TableDat

если один столбец равен нулю, вы получите пустой символ

Совместим с Microsoft SQL Server 2008+


0

Используйте функцию CONCAT, доступную в SQL Server 2012 и более поздних версиях.

SELECT CONCAT([FirstName], ' , ' , [LastName]) FROM YOURTABLE

0

Я попытался привести к строке и проверить строку нулевой длины, и это сработало.

CASE 
   WHEN LEN(CAST(field_value AS VARCHAR(MAX))) = 0 THEN 
       DO THIS
END AS field 

0

NULL не равняется ничему. Оператор case в основном говорит, что когда значение = NULL .. оно никогда не попадет.
Есть также несколько системных хранимых процедур, которые написаны неправильно с вашим синтаксисом. Смотрите sp_addpullsubscription_agent и sp_who2.
Хотел бы я знать, как уведомить Microsoft об этих ошибках, так как я не могу изменить системные хранимые процессы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.