[MSSQL]COALESCE与ISNULL函数
同事的一道面試題:
如何將某表中的某字段以逗號(hào)分隔拼接起來(lái)
在給出答案前,先給出測(cè)試用數(shù)據(jù),與之前的幾篇一樣:
--DROP TABLE T CREATE TABLE 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 union all SELECT 'a1','b1','c2',10 union all SELECT 'a1','b2','c2',40 union all SELECT 'a1','b2','c3',40 union all SELECT 'a1','b2','c3',50 union all ? SELECT 'a2','b3','c3',12 union all SELECT 'a2','b3','c3',22 union all 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函數(shù)
答案2:使用ISNULL函數(shù) DECLARE @T NVARCHAR(200) --SET @T = '' SELECT @T = ISNULL(@T,'') + GRP_A + ',' FROM T SELECT @T ? 輸出結(jié)果與上邊的一致哈不貼了
實(shí)際上,您應(yīng)該已經(jīng)發(fā)現(xiàn)了,這兩個(gè)函數(shù)其實(shí)是個(gè)障眼術(shù),即只要我的@T變量有初始化,完全可以直接拼接,算是答案3吧,代碼如下:
因?yàn)槲覀冎?在SQL中NULL表示UNKNOW類型,與任務(wù)字符串累加都會(huì)得到NULL值,如
那么,再回來(lái)看上述所謂的答案1,2,3都不夠嚴(yán)謹(jǐn)!即,如果該表中有那么一行,它的字段為NULL,會(huì)怎么辦?!
答案3最終會(huì)返回NULL,答案1和答案2則跳過(guò)NULL值所在行以前所有的數(shù)據(jù),返回NULL行以下的累加!所以嘞?要對(duì)GRP_A列進(jìn)行是否是NULL值的驗(yàn)證!
實(shí)事上,COALESCE函數(shù)與ISNULL函數(shù)原本就是這個(gè)功能:返回表達(dá)式中第一個(gè)不為NULL的值,所謂障眼術(shù)即指此
下邊的SQL腳本演示了兩個(gè)函數(shù)的基本功能:
SET NOCOUNT ON DECLARE @T CHAR(6) SELECT 'COALESCE',COALESCE(@T,NULL,NULL,'1234567890') SELECT 'ISNULL',ISNULL(@T,'1234567890') ? 輸出結(jié)果 -------- ---------- COALESCE 1234567890 ? ------ ------ ISNULL 123456
先聲明了一個(gè)類型為CHAR(6)的變量@T,沒有設(shè)置值,默認(rèn)為NULL
然后分別調(diào)用了ISNULL函數(shù)和COALESCE函數(shù),ISNULL返回了符合變量定義類型的值,即截?cái)嗪鬄镃HAR(6)類型,而COALESCE則返回完完整整的字符串
簡(jiǎn)單對(duì)比下兩個(gè)函數(shù)
兩個(gè)函數(shù)都返回第一個(gè)不為空的表達(dá)式,
其一,ISNULL考慮變量類型,而COALESCE則不考慮
其二,ISNULL只接收兩個(gè)參數(shù),而COALESCE則可以接收多個(gè)參數(shù)
再回來(lái)看那個(gè)面試題,
如何將某表中的某字段以逗號(hào)分隔拼接起來(lái)?
答案4:
DECLARE @T NVARCHAR(200) ? SET @T = '' SELECT @T = @T + ISNULL(GRP_A,'NULL') + ',' FROM T SELECT @T面試題這一部分結(jié)束,來(lái)看看ISNULL函數(shù)的應(yīng)用實(shí)例
1,利用ISNULL函數(shù)干掉OR運(yùn)算!
題目是查詢表中VAL小于20的值,包括NULL值:
SELECT * FROM T WHERE ISNULL(VAL,-1) < 20 SELECT * FROM T WHERE VAL IS NULL OR VAL < 20 ? 兩個(gè)SQL具有相同的輸出結(jié)果 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非主流更新表存儲(chǔ)過(guò)程示例
如某更新表存儲(chǔ)過(guò)程如下:
CREATE PROC 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當(dāng)我們使用這個(gè)存儲(chǔ)過(guò)程的時(shí)候,必須先得該行的所有記錄,再把所有記錄更新回去,可是這并不總是必須的
有時(shí)候手頭只有兩個(gè)數(shù)據(jù):ID和VAL,我只想更新這個(gè)VAL
又有時(shí)候手頭有另外兩個(gè)數(shù)據(jù):ID和GRP_A,這時(shí)候只更新GRP_A列即可
還有很多情況,如僅更新GRP_A,
僅更新GRP_A,GRP_B
僅更新GRP_A,GRP_B,GRP_C
僅更新GRP_A,GRP_B,GRP_C,VAL
...
這樣的組合太多了,要想一勞永逸解決問(wèn)題那就得更新任何字段前,先得到整行記錄,再整行更新回去,于是多了一項(xiàng)工作:先查詢,再更新
不爽不爽,那沒有辦法不先查詢直接更新某一列呢?而且列可以任意組合?
在給出答案前,先聲明一句:這個(gè)方法算不上完美解決方案,僅僅是個(gè)思路罷了,雖然我一直認(rèn)為沒什么影響,但如果要在正式項(xiàng)目中使用,建議還是多聽聽DBA的意見!
非主流更新任意列存儲(chǔ)過(guò)程:
CREATE PROC 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上邊的這個(gè)存儲(chǔ)過(guò)程,假設(shè)參數(shù)@GRP_A為NULL時(shí),經(jīng)過(guò)ISNULL運(yùn)算返回了GRP_A列!即實(shí)際變成了
SET GRP_A = ISNULL(NULL,GRP_A)
再演變?yōu)镾ET GRP_A = GRP_A!神馬意思?什么也沒更新…把自己更新為自己,等什么也沒干,空忙活一場(chǎng)!但是
我們的效果達(dá)到了!@GRP_A參數(shù)為NULL時(shí)(不傳遞該參數(shù),在定義存儲(chǔ)過(guò)程時(shí)已經(jīng)設(shè)計(jì)為可選參數(shù)),自己更新自己
當(dāng)該參數(shù)不為NULL時(shí),進(jìn)行了實(shí)際的更新,其余三列以此類推,除@ID參數(shù)必須要傳外,其它參數(shù)都是可選的!誰(shuí)有值就更新誰(shuí),
什么模式?門面模式(又稱外觀模式),把小碎操作變成一個(gè)大的操作
解讀2為什么第二部分都使用了ISNULL而不是COALESCE函數(shù)?
原因正是ISNULL會(huì)考慮第一個(gè)參數(shù)的類型聲明從而自動(dòng)截?cái)喑L(zhǎng)部分?jǐn)?shù)據(jù)!
如果用COALESCE的話可能會(huì)導(dǎo)致返回結(jié)果超出列定義!
產(chǎn)生將截?cái)喽M(jìn)制字符串錯(cuò)誤
供討論
總結(jié)
以上是生活随笔為你收集整理的[MSSQL]COALESCE与ISNULL函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: JavaScript窗体控制函数
- 下一篇: NHibernate初学者指南(6):映