其他函式
除了數學函式、字串函式、日期函式之外,資料庫中還有其他一些函式,比如進行型別轉換的函式、進行非空邏輯判斷的函式等,這些函式也是非常重要的,因此在本節中我們將對這些函式進行介紹。
- 型別轉換
在使用SQL語句的時候,我們使用的資料的型別不一定符合函式或者運算子的需要,比如函式需要整數型別的資料而我們使用的則是一個字串,在一些情況下資料庫系統會替我們自動將字串型別轉換為整數型別,這種轉換稱為隱式轉換。但是在有的情況下資料庫系統不會進行隱式轉換,這時就要使用型別轉換函數了,這種轉換稱為顯式轉換。使用型別轉換函式不僅可以保證型別轉換的正確性,而且可以提高資料處理的速度,因此應該使用顯式轉換,儘量避免使用隱式轉換。
在主流資料庫系統中都提供了型別轉換函式,下面分別進行介紹。
- MYSQL
MYSQL中提供了CAST()函式和CONVERT()函式用於進行型別轉換,CAST()是符合ANSI SQL99的函式,CONVERT() 是符合ODBC標準的函式,這兩個函式只是引數的呼叫方式略有差異,其功能幾乎相同。這兩個函式的引數格式如下:
CAST(expression AS type)
CONVERT(expression,type)
引數expression為待進行型別轉換的表示式,而type為轉換的目標型別,type可以是下面的任一個:
可選值縮寫說明 BINARY BINARY字串 CHAR 字串型別 DATE 日期型別 DATETIME 時間日期型別 SIGNED INTEGER SIGNED 有符號整數 TIME 時間型別 UNSIGNED INTEGER UNSIGNED 無符號整數
下面的SQL語句分別演示以有符號整形、無符號整形、日期型別、時間型別為目標型別的資料轉換:
SELECT CAST("-30" AS SIGNED) as sig,CONVERT ("36", UNSIGNED INTEGER) as usig,CAST("2008-08-08" AS DATE) as d,CONVERT ("08:09:10", TIME) as t
執行完畢我們就能在輸出結果中看到下面的執行結果:
sig usig d t
-30 36 2008-08-08 08:09:10
- MSSQLServer
與MYSQL類似,MSSQLServer中同樣提供了名稱為CAST()和CONVERT()兩個函式用於進行型別轉換,CAST()是符合ANSI SQL99的函式,CONVERT() 是符合ODBC標準的函式。
與MYSQL中的CONVERT()函式不同的是MSSQLServer中的CONVERT()函式引數順序正好與MYSQL中的CONVERT()函式引數順序相反。這兩個函式的引數格式如下:
CAST ( expression AS data_type)
CONVERT ( data_type, expression)
引數expression為待進行型別轉換的表示式,而type為轉換的目標型別,與MYSQL不同,MYSQLServer中的目標型別幾乎可以是資料庫系統支援的任何型別。
下面的SQL語句分別演示以整形、數值、日期時間型別為目標型別的資料轉換:
SELECT CAST("-30" AS INTEGER) as i,CONVERT(DECIMAL,"3.1415726") as d,CONVERT(DATETIME,"2008-08-08 08:09:10") as dt
執行完畢我們就能在輸出結果中看到下面的執行結果:
i d dt
-30 3 2008-08-08 08:09:10.0
下面的SQL語句用於將每個人的身份證後三位轉換為整數型別並且進行相關的計算:
SELECT FIdNumber,RIGHT(FIdNumber,3) as 後三位,CAST(RIGHT(FIdNumber,3) AS INTEGER) as 後三位的整數形式,CAST(RIGHT(FIdNumber,3) AS INTEGER)+1 as 後三位加1,CONVERT(INTEGER,RIGHT(FIdNumber,3))/2 as 後三位除以2 FROM T_Person
- Oracle
Oracle中也有一個名稱為CONVERT()的函式,不過這個函式是用來進行字符集轉換的。Oracle中不支援用做資料型別轉換的CAST()和CONVERT()兩個函式,它提供了針對性更強的型別TO_CHAR()、TO_DATE()、TO_NUMBER()等函式,這些函式可以將資料顯式的轉換為字串型別、日期時間型別或者數值型別。Oracle中還提供了HEXTORAW()、RAWTOHEX()、TO_MULTI_BYTE()、TO_SINGLE_BYTE()等函式用於儲存格式的轉換。
下面我們將對這些函式進行分別介紹。
1) TO_CHAR()
TO_CHAR()函式用來將時間日期型別或者數值型別的資料轉換為字串,其引數格式如下:
TO_CHAR(expression,format)
引數expression為待轉換的表示式,引數format為轉換後的字串格式,引數format可以省略,如果省略引數format將會按照資料庫系統內建的轉換規則進行轉換。引數format的可以採用的格式非常豐富,具體可以參考Oracle的聯機文件。
下面的SQL語句將出生日期和身高按照不同的格式轉換為字串型別:
SELECT FBirthDay,TO_CHAR(FBirthDay,"YYYY-MM-DD") as c1,FWeight,TO_CHAR(FWeight,"L99D99MI") as c2,TO_CHAR(FWeight) as c3 FROM T_Person
2) TO_DATE()
TO_DATE()函式用來將字串轉換為時間型別,其引數格式如下:
TO_DATE (expression,format)
引數expression為待轉換的表示式,引數format為轉換格式,引數format可以省略,如果省略引數format將會按照資料庫系統內建的轉換規則進行轉換。
下面的SQL語句用於將字串形式的資料按照特定的格式解析為日期型別:
SELECT TO_DATE("2008-08-08 08:09:10", "YYYY-MM-DD HH24:MI:SS") as dt1,TO_DATE("20080808 080910", "YYYYMMDD HH24MISS") as dt2 FROM DUAL
執行完畢我們就能在輸出結果中看到下面的執行結果:
DT1 DT2
2008-08-08 08:09:10.0 2008-08-08 08:09:10.0
3) TO_NUMBER()
TO_NUMBER()函式用來將字串轉換為數值型別,其引數格式如下:
TO_NUMBER (expression,format)
引數expression為待轉換的表示式,引數format為轉換格式,引數format可以省略,如果省略引數format將會按照資料庫系統內建的轉換規則進行轉換。引數format的可以採用的格式非常豐富,具體可以參考Oracle的聯機文件。
下面的SQL語句用於將字串形式的資料按照特定的格式解析為數值型別:
SELECT TO_NUMBER("33.33") as n1,TO_NUMBER("100.00", "9G999D99") as n2 FROM DUAL
執行完畢我們就能在輸出結果中看到下面的執行結果:
N1 N2
33.33 100.55
4) HEXTORAW()、RAWTOHEX()
HEXTORAW()用於將十六進位制格式的資料轉換為原始值,而RAWTOHEX()函式用來將原始值轉換為十六進位制格式的資料。例子如下:
SELECT HEXTORAW("7D"),RAWTOHEX ("a"),HEXTORAW(RAWTOHEX("w")) FROM DUAL
執行完畢我們就能在輸出結果中看到下面的執行結果:
HEXTORAW(7D) RAWTOHEX(A) HEXTORAW(RAWTOHEX(W))
} 61 w
5) TO_MULTI_BYTE()、TO_SINGLE_BYTE()
TO_MULTI_BYTE()函式用於將字串中的半形字元轉換為全形字元,而TO_SINGLE_BYTE()函式則用來將字串中的全形字元轉換為半形字元。例子如下:
SELECT TO_MULTI_BYTE("moring"),TO_SINGLE_BYTE("hello") FROM DUAL
執行完畢我們就能在輸出結果中看到下面的執行結果:
TO_MULTI_BYTE(MORING) TO_SINGLE_BYTE(HELLO)
moring hello
- DB2
DB2中沒有提供專門進行顯式型別轉換的函式,取而代之的是借用了很多高階語言中的強制型別轉換的概念,也就是使用目標型別名做為函式名來進行型別轉換,比如要將expr轉換為日期型別,那麼使用DATE(expr)即可。這種實現機制非常方便,降低了學習難度。
下面的SQL語句展示了DB2中型別轉換的方式:
SELECT CHAR(FRegDay),INT("33"),DOUBLE("-3.1415926") FROM T_Person
- 空值處理
在資料庫中經常需要對空值(NULL)做處理,比如“如果名稱為空值則返回別名”,甚至還有更復雜的需求,比如“如果名稱為空值則返回別名,如果別名也為空則返回‘佚名’兩個字”、“如果名稱為與別名相等則返回空值,否則返回名稱”。這些需求已經帶有流程控制的色彩了,一般來說需要在宿主語言中使用流程控制語句來進行處理,可是如果是在報表程式等大資料量的程式中把這些任務交給宿主語言的話會大大降低執行速度,因此我們必須想辦法在SQL這一層進行處理。
為了更好的演示本節中的例子,我們需要對T_Person表中的資料進行一下修改,也就是將Kerry的出生日期修改為空值,將Smith的出生日期和註冊日期都修改為空值,執行下面的SQL語句:
UPDATE T_Person SET FBirthDay=nullWHERE FName="Kerry";
UPDATE T_Person SET FBirthDay=null AND FRegDay=nullWHERE FName="Smith";
- COALESCE()函式
主流資料庫系統都支援COALESCE()函式,這個函式主要用來進行空值處理,其引數格式如下:
COALESCE ( expression,value1,value2……,valuen)
COALESCE()函式的第一個引數expression為待檢測的表示式,而其後的引數個數不定。
COALESCE()函式將會返回包括expression在內的所有引數中的第一個非空表示式。如果expression不為空值則返回expression;否則判斷value1是否是空值,如果value1不為空值則返回value1;否則判斷value2是否是空值,如果value2不為空值則返回value3;……以此類推,如果所有的表示式都為空值,則返回NULL。
我們將使用COALESCE()函式完成下面的功能,返回人員的“重要日期”:如果出生日期不為空則將出生日期做為“重要日期”,如果出生日期為空則判斷註冊日期是否為空,如果註冊日期不為空則將註冊日期做為“重要日期”,如果註冊日期也為空則將“2008年8月8
日”做為“重要日期”。實現此功能的SQL語句如下:
MYSQL、MSSQLServer、DB2:
SELECT FName,FBirthDay,FRegDay,COALESCE(FBirthDay,FRegDay,"2008-08-08") AS ImportDay FROM T_Person Oracle: SELECT FBirthDay,FRegDay,COALESCE(FBirthDay,FRegDay,TO_DATE("2008-08-08", "YYYY-MM-DD HH24:MI:SS")) AS ImportDay FROM T_Person
這裡邊最關鍵的就是Kerry和Smith這兩行,可以看到這裡的計算邏輯是完全符合我們的需求的。
- NULLIF()函式
主流資料庫都支援NULLIF()函式,這個函式的引數格式如下:
NULLIF ( expression1 , expression2 )
如果兩個表示式不等價,則NULLIF返回第一個expression1的值。如果兩個表示式等價,則NULLIF返回第一個expression1型別的空值。也就是返回型別與第一個expression相同。
下面的SQL演示了NULLIF()函式的用法:
SELECT FBirthDay,FRegDay,NULLIF(FBirthDay,FRegDay) FROM T_Person
- CASE函式
COALESCE()函式只能用來進行空值的邏輯判斷處理,如果要實現“如果年齡大於25則返回姓名,否則返回別名”這樣的邏輯判斷就比較麻煩了。在主流資料庫系統中提供了CASE函式的支援,嚴格意義上來講CASE函式已經是流程控制語句了,不是簡單意義上的函式,不過為了方便,很多人都將CASE稱作“流程控制函式”。
CASE函式有兩種用法,下面分別介紹。
- 用法一
CASE函式的語法如下:
CASE expression
WHEN value1 THEN returnvalue1
WHEN value2 THEN returnvalue2
WHEN value3 THEN returnvalue3
……
ELSE defaultreturnvalue
END
CASE函式對錶達式expression進行測試,如果expression等於value1則返回returnvalue1,如果expression等於value2則返回returnvalue2,expression等於value3則返回returnvalue3,…… 以此類推,如果不符合所有的WHEN條件,則返回預設值defaultreturnvalue。
可見CASE函式和普通程式語言中的SWITCH……CASE語句非常類似。使用CASE函式我們可以實現非常複雜的業務邏輯。下面的SQL用於判斷誰是“好孩子”,我們比較偏愛Tom和Lily,所以我們將他們認為是好孩子,而我們比較不喜歡Sam和Kerry,所以認為他們是壞孩子,其他孩子則為普通孩子:
SELECT FName,(CASE FName
WHEN "Tom" THEN "GoodBoy" WHEN "Lily" THEN "GoodGirl" WHEN "Sam" THEN "BadBoy" WHEN "Kerry" THEN "BadGirl" ELSE "Normal" END) as isgood FROM T_Person
執行完畢我們就能在輸出結果中看到下面的執行結果:
FNAME ISGOOD
Tom GoodBoy
Jim Normal
Lily GoodGirl
Kelly Normal
Sam BadBoy
Kerry BadGirl
Smith Normal
BillGates Normal
CASE函式在製作報表的時候非常有用。比如表T_Customer中的FLevel欄位是整數型別,它記錄了客戶的級別,如果為1則是VIP客戶,如果為2則是高階客戶,如果為3則是普通客戶,在製作報表的時候顯然不應該把1、2、3這樣的數字顯示到報表中,而應該顯示相應的文字,這裡就可以使用CASE函式進行處理,SQL語句如下:
SELECT FName,(CASE FLevel
WHEN 1 THEN "VIP客戶" WHEN 2 THEN "高階客戶" WHEN 3 THEN "普通客戶" ELSE "客戶型別錯誤" END) as FLevelName FROM T_Customer
- 用法二
上邊一節中介紹的CASE語句的用法只能用來實現簡單的“等於”邏輯的判斷,要實現“如果年齡小於18則返回‘未成年人’,否則返回‘成年人’”是無法完成的。值得慶幸的是,CASE函式還提供了第二種用法,其語法如下:
CASE
WHEN condition1 THEN returnvalue1
WHEN condition 2 THEN returnvalue2
WHEN condition 3 THEN returnvalue3
……
ELSE defaultreturnvalue
END
其中的condition1 、condition 2、condition 3……為條件表示式,CASE函式對各個表示式從前向後進行測試,如果條件condition1為真則返回returnvalue1,否則如果條件condition2為真則返回returnvalue2,否則如果條件condition3為真則返回returnvalue3,……以此類推,如果不符合所有的WHEN條件,則返回預設值defaultreturnvalue。
這種用法中沒有限制只能對一個表示式進行判斷,因此使用起來更加靈活。比如下面的SQL語句用來判斷一個人的體重是否正常,如果體重小於40則認為太瘦,而如果體重大於50則認為太胖,介於40和50之間則認為是正常:
SELECT FName,FWeight,
(CASE
WHEN FWeight<40 THEN "thin" WHEN FWeight>50 THEN "fat" ELSE "ok" END) as isnormal FROM T_Person