[MSSQL]COALESCE与ISNULL函数
同事的一道面試題:
如何將某表中的某字段以逗號分隔拼接起來
在給出答案前,先給出測試用數據,與之前的幾篇一樣:
--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函數
答案2:使用ISNULL函數 DECLARE @T NVARCHAR(200) --SET @T = '' SELECT @T = ISNULL(@T,'') + GRP_A + ',' FROM T SELECT @T ? 輸出結果與上邊的一致哈不貼了
實際上,您應該已經發現了,這兩個函數其實是個障眼術,即只要我的@T變量有初始化,完全可以直接拼接,算是答案3吧,代碼如下:
因為我們知道,在SQL中NULL表示UNKNOW類型,與任務字符串累加都會得到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 IS NULL OR 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非主流更新表存儲過程示例
如某更新表存儲過程如下:
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當我們使用這個存儲過程的時候,必須先得該行的所有記錄,再把所有記錄更新回去,可是這并不總是必須的
有時候手頭只有兩個數據: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的意見!
非主流更新任意列存儲過程:
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上邊的這個存儲過程,假設參數@GRP_A為NULL時,經過ISNULL運算返回了GRP_A列!即實際變成了
SET GRP_A = ISNULL(NULL,GRP_A)
再演變為SET GRP_A = GRP_A!神馬意思?什么也沒更新…把自己更新為自己,等什么也沒干,空忙活一場!但是
我們的效果達到了!@GRP_A參數為NULL時(不傳遞該參數,在定義存儲過程時已經設計為可選參數),自己更新自己
當該參數不為NULL時,進行了實際的更新,其余三列以此類推,除@ID參數必須要傳外,其它參數都是可選的!誰有值就更新誰,
什么模式?門面模式(又稱外觀模式),把小碎操作變成一個大的操作
解讀2為什么第二部分都使用了ISNULL而不是COALESCE函數?
原因正是ISNULL會考慮第一個參數的類型聲明從而自動截斷超長部分數據!
如果用COALESCE的話可能會導致返回結果超出列定義!
產生將截斷二進制字符串錯誤
供討論
總結
以上是生活随笔為你收集整理的[MSSQL]COALESCE与ISNULL函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaScript窗体控制函数
- 下一篇: 做梦梦到金银元宝是什么意思