Предложение 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.