[MSSQL]COALESCE与ISNULL函数

同事的一道面试题:

如何将某表中的某字段以逗号分隔拼接起来

在给出答案前,先给出测试用数据,与之前的几篇一样:

--DROPTABLE T
CREATETABLE T(GRP_A VARCHAR(20),GRP_B VARCHAR(20),GRP_C VARCHAR(20),VAL INT)
INSERT INTO T(GRP_A,GRP_B,GRP_C,VAL)
SELECT'a1','b1','c1',10 unionall
SELECT'a1','b1','c2',10 unionall
SELECT'a1','b2','c2',40 unionall
SELECT'a1','b2','c3',40 unionall
SELECT'a1','b2','c3',50 unionall
 
   
SELECT'a2','b3','c3',12 unionall
SELECT'a2','b3','c3',22 unionall
SELECT'a2','b3','c3',32 
 
   
SELECT * FROM T 
 
   
GRP_A                GRP_B                GRP_C                VAL
-----------------------------------------------------------------------
a1                   b1                   c1                   10
a1                   b1                   c2                   10
a1                   b2                   c2                   40
a1                   b2                   c3                   40
a1                   b2                   c3                   50
a2                   b3                   c3                   12
a2                   b3                   c3                   22
a2                   b3                   c3                   32
 
   
(8 行受影响)


答案1:使用COALESCE函数

DECLARE @T NVARCHAR(200)
--SET @T = ''
SELECT @T = COALESCE(@T,'') + GRP_A + ','FROM T
SELECT @T
 
   
输出结果
------------------------
a1,a1,a1,a1,a1,a2,a2,a2,

答案2:使用ISNULL函数
DECLARE @T NVARCHAR(200)
--SET @T = ''
SELECT @T = ISNULL(@T,'') + GRP_A + ','FROM T
SELECT @T
 
   
输出结果与上边的一致哈不贴了


实际上,您应该已经发现了,这两个函数其实是个障眼术,即只要我的@T变量有初始化,完全可以直接拼接,算是答案3吧,代码如下:

DECLARE @T NVARCHAR(200)
SET @T = ''
SELECT @T = @T + GRP_A + ','FROM T
SELECT @T
 
   
输出结果与上上例一致不贴了


因为我们知道,在SQL中NULL表示UNKNOW类型,与任务字符串累加都会得到NULL值,如

DECLARE @T NVARCHAR(200)
SET @T = NULL
SET @T = @T + 'helloworld'
--PRINT @T这里不用PRINT是因为看不到实际输出什么
SELECT @T
 
   
实际输出
----
NULL


那么,再回来看上述所谓的答案1,2,3都不够严谨!即,如果该表中有那么一行,它的字段为NULL,会怎么办?!

答案3最终会返回NULL,答案1和答案2则跳过NULL值所在行以前所有的数据,返回NULL行以下的累加!所以嘞?要对GRP_A列进行是否是NULL值的验证!

实事上,COALESCE函数与ISNULL函数原本就是这个功能:返回表达式中第一个不为NULL的值,所谓障眼术即指此

下边的SQL脚本演示了两个函数的基本功能:

SET NOCOUNT ON
DECLARE @T CHAR(6)
SELECT'COALESCE',COALESCE(@T,NULL,NULL,'1234567890')
SELECT'ISNULL',ISNULL(@T,'1234567890')
 
   
输出结果
------------------
COALESCE 1234567890
 
   
------------
ISNULL 123456


先声明了一个类型为CHAR(6)的变量@T,没有设置值,默认为NULL

然后分别调用了ISNULL函数和COALESCE函数,ISNULL返回了符合变量定义类型的值,即截断后为CHAR(6)类型,而COALESCE则返回完完整整的字符串

简单对比下两个函数

两个函数都返回第一个不为空的表达式,

其一,ISNULL考虑变量类型,而COALESCE则不考虑

其二,ISNULL只接收两个参数,而COALESCE则可以接收多个参数

再回来看那个面试题,

如何将某表中的某字段以逗号分隔拼接起来?

答案4:

DECLARE @T NVARCHAR(200)
 
   
SET @T = ''
SELECT @T = @T + ISNULL(GRP_A,'NULL') + ','FROM T
SELECT @T

面试题这一部分结束,来看看ISNULL函数的应用实例

1,利用ISNULL函数干掉OR运算!

题目是查询表中VAL小于20的值,包括NULL值:

SELECT * FROM T WHERE ISNULL(VAL,-1) < 20
SELECT * FROM T WHERE VAL ISNULLOR VAL < 20
 
   
两个SQL具有相同的输出结果
GRP_A                GRP_B                GRP_C                VAL
-----------------------------------------------------------------------
a1                   b1                   c1                   10
a1                   b1                   c2                   10
a2                   b3                   c3                   NULL
a2                   b3                   c3                   NULL
a2                   b3                   c3                   NULL
 
   
(5 行受影响)
 
   
GRP_A                GRP_B                GRP_C                VAL
-----------------------------------------------------------------------
a1                   b1                   c1                   10
a1                   b1                   c2                   10
a2                   b3                   c3                   NULL
a2                   b3                   c3                   NULL
a2                   b3                   c3                   NULL
 
   
(5 行受影响)


2,ISNULL非主流更新表存储过程示例

如某更新表存储过程如下:

CREATEPROC UpdateT(
    @ID INT,
    @GRP_A VARCHAR(10) = NULL,
    @GRP_B VARCHAR(10) = NULL,
    @GRP_C VARCHAR(10) = NULL,
    @VAL INT = 0
)AS
BEGIN
UPDATE T SET
        GRP_A = @GRP_A,
        GRP_B = @GRP_B,
        GRP_C = @GRP_C,
        VAL = @VAL
WHERE ID = @ID
END

当我们使用这个存储过程的时候,必须先得该行的所有记录,再把所有记录更新回去,可是这并不总是必须的

有时候手头只有两个数据:ID和VAL,我只想更新这个VAL

又有时候手头有另外两个数据:ID和GRP_A,这时候只更新GRP_A列即可

还有很多情况,如仅更新GRP_A,

仅更新GRP_A,GRP_B

仅更新GRP_A,GRP_B,GRP_C

仅更新GRP_A,GRP_B,GRP_C,VAL

...

这样的组合太多了,要想一劳永逸解决问题那就得更新任何字段前,先得到整行记录,再整行更新回去,于是多了一项工作:先查询,再更新

不爽不爽,那没有办法不先查询直接更新某一列呢?而且列可以任意组合?

在给出答案前,先声明一句:这个方法算不上完美解决方案,仅仅是个思路罢了,虽然我一直认为没什么影响,但如果要在正式项目中使用,建议还是多听听DBA的意见!

非主流更新任意列存储过程:

CREATEPROC UpdateT(
    @ID INT,
    @GRP_A VARCHAR(10) = NULL,
    @GRP_B VARCHAR(10) = NULL,
    @GRP_C VARCHAR(10) = NULL,
    @VAL INT = 0
)AS
BEGIN
UPDATE T SET
        GRP_A = ISNULL(@GRP_A,GRP_A),
        GRP_B = ISNULL(@GRP_B,GRP_B),
        GRP_C = ISNULL(@GRP_C,GRP_C),
        VAL = ISNULL(@VAL,VAL)
WHERE ID = @ID
END

解读1上边的这个存储过程,假设参数@GRP_A为NULL时,经过ISNULL运算返回了GRP_A列!即实际变成了

SETGRP_A=ISNULL(NULL,GRP_A)

再演变为SETGRP_A=GRP_A!神马意思?什么也没更新…把自己更新为自己,等什么也没干,空忙活一场!但是

我们的效果达到了!@GRP_A参数为NULL时(不传递该参数,在定义存储过程时已经设计为可选参数),自己更新自己

当该参数不为NULL时,进行了实际的更新,其余三列以此类推,除@ID参数必须要传外,其它参数都是可选的!谁有值就更新谁,

什么模式?门面模式(又称外观模式),把小碎操作变成一个大的操作

解读2为什么第二部分都使用了ISNULL而不是COALESCE函数?

原因正是ISNULL会考虑第一个参数的类型声明从而自动截断超长部分数据!

如果用COALESCE的话可能会导致返回结果超出列定义!

产生将截断二进制字符串错误

http://www.cnblogs.com/kkun/archive/2011/08/17/2143220.html

你可能感兴趣的:([MSSQL]COALESCE与ISNULL函数)