T-SQL CASE子句:如何指定WHEN NULL


227

我写了一个类似这样的T-SQL语句(原始语句看起来不一样,但是我想在这里举一个简单的例子):

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

该语句没有任何语法错误,但大小写始终选择ELSE部分-如果last_name为null。但为什么?

我想做的是将first_name和last_name结合在一起,但是如果last_name为null,则整个名称将变为null:

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

您知道问题出在哪里吗?

Answers:


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

4
@Luther的COALESCE建议比我的回答要好。它的效率略低,但更优雅。
马塞洛·坎托斯

用类似这样的方式来处理它可能是有用的或类似的东西:COALESCE(last_name,'')case语句虽然简洁,但在大型查询中比COALESCE的可维护性差。最后,您将得到相同的结果。如果您需要优化,请检查执行计划,但我没有注意到有很大的不同。
安东尼·梅森

1
COALESCE基本上在内部翻译成CASE。CASE的问题是last_name被评估两次,并且可能导致其他副作用-请参阅这篇出色的文章。为了解决所提出的问题,我希望ISNULL(' '+ last_name, '')在下面的评论中提及。
miroxlav '16

41

WHEN部分与==进行了比较,但您实际上无法与NULL进行比较。尝试

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

代替或避免:

COALESCE(' '+last_name,'')

(“ last_name为NULL时,''+ last_name为NULL,因此在这种情况下应返回”)


好的,谢谢您提供的WHEN部分中带有==的信息。我不知道 使用COALESCE,它也可以正常工作。没想到有这么多的可能性。
meni 2010年

15

有很多解决方案,但是没有一个解决方案可以说明为什么原始语句不起作用。

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

在when之后,将检查是否相等,该检查应为true或false。

如果比较的一个或两个部分为空,则比较的结果将为UNKNOWN,在个案结构中将其视为false。参见:https : //www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/

为了避免这种情况,Coalcece是最好的方法。


11

根据您的查询,您还可以执行以下操作:

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

2
当last_name为null时,这会添加一个冗余空间,这是OP试图避免的空间。
马塞洛·坎托斯

6
更好地使用ISNULL(' '+ last_name, '')以防止多余的空间。
2010年

是的,我发布后就意识到了。我想我会离开的,因为它解决了他遇到的麻烦。
伊恩·雅各布斯

7

问题在于null不被视为等于其自身,因此该子句永不匹配。

您需要显式检查null:

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,则整个空格+姓氏都将变为NULL,并且您只会得到一个名字,否则将获得首字母+空格+姓氏。

只要设置了用于使用空字符串的串联的默认设置,它就可以工作:

SET CONCAT_NULL_YIELDS_NULL ON 

不必担心,因为OFF在以后的SQl Server版本中该模式将消失


4

问题在于,NULL甚至不等于自身都不被认为等于任何东西,但是奇怪的是它也不等于自身。

请考虑以下语句(在SQL Server T-SQL中BTW是非法的,但在My-SQL中是有效的,但这是ANSI为null定义的内容,即使在SQL Server中也可以使用case语句等进行验证。)

SELECT NULL = NULL -- Results in NULL

SELECT NULL <> NULL -- Results in NULL

因此,该问题没有真/假答案,答案也为空。

这有很多含义,例如

  1. CASE语句,其中除非您使用显式的任何空值将始终使用ELSE子句当NULL条件(NOTWHEN NULL条件
  2. 字符串串联,为
    SELECT a + NULL -- Results in NULL
  3. 在WHERE IN或WHERE NOT IN子句中,好像想要正确的结果一样,请确保在相关的子查询中过滤掉所有空值。

可以通过指定SET ANSI_NULLS OFF,在SQL Server中覆盖此行为,但是建议这样做,也不应这样做,因为它可能会引起许多问题,仅仅是因为标准的偏差。

(作为补充,在My-SQL中,可以选择使用特殊运算符<=>进行空比较。)

相比之下,在一般的编程语言中,将null视为一个正则值并等于其自身,但是NAN值也不等于其自身,但至少将其与自身进行比较时返回“ false”(和当检查不相等时,不同的编程语言具有不同的实现)。

但是请注意,在基本语言(例如VB等)中,没有'null'关键字,而是使用了'Nothing'关键字,该关键字不能用于直接比较,而需要在SQL中使用'IS',但是实际上它等于自身(使用间接比较时)。


1
“奇怪的是,它也不等于自身。” =>考虑到SQL中的null具有“未知”的含义,这并不奇怪。未知的东西很可能与其他未知的东西不同。
JDC

1
CASE  
    WHEN last_name IS null THEN '' 
    ELSE ' ' + last_name 
END

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输入的时候我的测试证实,也LEN(),即LEN真(NULL)返回NULL,不是0返回NULL
贾森马斯格罗夫

Pokechu22是正确的,这是已更正的脚本:CASE LEN(ISNULL(last_Name,''))0时” ELSE''+ last_name END AS newlastName
Chef Slagle 2016年

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

甲骨文:

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

使用SQL Server 2012及更高版本中可用的CONCAT函数。

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,因为我无法更改系统存储的进程。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.