《MySQL 入門教程》第 13 篇 CASE 表達式與控制流函數

MySQL 爲 SQL 語句提供了類似 if-then-else 的邏輯處理功能,可以基於不同的條件返回不同的結果。這些功能包括 CASE 表達式和一些控制流函數。

13.1 CASE 表達式

CASE 表達式支持兩種形式:簡單 CASE 表達式和搜索 CASE 表達式。

13.1.1 簡單 CASE 表達式

簡單 CASE 表達式的語法如下:

CASE expression
  WHEN value1 THEN result1
  WHEN value2 THEN result2
  ...
  [ELSE default_result]
END

表達式的計算過程如下圖所示:

simple case
首先計算 expression 的值;然後依次與 WHEN 列表中的值(value1,value2,…)進行比較,找到第一個相等的值並返回對應的結果(result1,result2,…);如果沒有找到相等的值,返回 ELSE 中的默認結果;如果此時沒有指定 ELSE,返回 NULL 值。

以下語句使用簡單 CASE 表達式將員工的部門編號顯示爲相應的部門名稱:

select emp_name as "員工姓名",
       case dept_id
         when 1 then '行政管理部'
         when 2 then '人力資源部'
         when 3 then '財務部'
         when 4 then '研發部'
         when 5 then '銷售部'
         when 6 then '保衛部'
         else '其他部門'
       end as "所在部門"
from employee;
員工姓名|所在部門 |
------|--------|
劉備   |行政管理部|
關羽   |行政管理部|
張飛   |行政管理部|
...
鄧芝   |銷售部   |
簡雍   |銷售部   |
孫乾   |銷售部   |

首先,判斷部門編號是否等於 1,等於就顯示爲“行政管理部”;否則,如果部門編號等於 2, 顯示爲“人力資源部”;依次類推;如果部門編號不等於 1 到 6 中的任何值,顯示爲“其他部門”。

CASE 表達式的一個常見應用就是實現表的行列轉換。創建以下學生成績表:

-- 創建成績表 t_case,sname 爲學生姓名,cname 爲課程名稱,score 爲考試成績
CREATE TABLE t_case(sname varchar(10), cname varchar(10), score int);

-- 插入測試數據
INSERT INTO t_case(sname, cname, score) VALUES ('張三', '語文', 80);
INSERT INTO t_case(sname, cname, score) VALUES ('李四', '語文', 77);
INSERT INTO t_case(sname, cname, score) VALUES ('王五', '語文', 91);
INSERT INTO t_case(sname, cname, score) VALUES ('張三', '數學', 85);
INSERT INTO t_case(sname, cname, score) VALUES ('李四', '數學', 90);
INSERT INTO t_case(sname, cname, score) VALUES ('王五', '數學', 60);
INSERT INTO t_case(sname, cname, score) VALUES ('張三', '英語', 81);
INSERT INTO t_case(sname, cname, score) VALUES ('李四', '英語', 69);
INSERT INTO t_case(sname, cname, score) VALUES ('王五', '英語', 82);

該表中的數據如下:

select *
from t_case;
sname |cname  |score|
------|-------|-----|
張三   |語文   |   80|
李四   |語文   |   77|
王五   |語文   |   91|
張三   |數學   |   85|
李四   |數學   |   90|
王五   |數學   |   60|
張三   |英語   |   81|
李四   |英語   |   69|
王五   |英語   |   82|

接下來我們利用 CASE 表達式將其轉換爲按列顯示的形式:

select sname,
       sum(case cname when '語文' then score else 0 end) as "語文",
       sum(case cname when '數學' then score else 0 end) as "數學",
       sum(case cname when '英語' then score else 0 end) as "英語"
from t_case
group by sname;
sname |語文|數學|英語|
------|---|---|---|
張三   | 80| 85| 81|
李四   | 77| 90| 69|
王五   | 91| 60| 82|

第一個 CASE 表達式用於獲取學生的語文成績,cname 等於“語文”就返回考試成績,不是“語文”就記爲 0 分;第二個和第三個 CASE 表達式分別用於獲取數學和英語成績。然後,使用 SUM 彙總函數和 GROUP BY 分組操作將每個學生的成績合併成一條記錄。

簡單 CASE 表達式使用的是等值比較(=),只能處理簡單的邏輯。如果想要進行復雜的邏輯處理,例如根據考試成績評出優秀、良好、及格等,或者判斷表達式的值是否爲空,就需要使用更加強大的搜索 CASE 表達式。

13.1.2 搜索 CASE 表達式

搜索 CASE 表達式的語法如下:

CASE
  WHEN condition1 THEN result1
  WHEN condition2 THEN result2
  ...
  [ELSE default_result]
END

表達式的計算過程如下圖所示:

search case
按照順序依次計算每個分支中的條件(condition1,condition2,…),找到第一個結果爲真的分支並返回相應的結果(result1,result2,…);如果沒有任何條件爲真,返回 ELSE 中的默認結果;如果此時沒有指定 ELSE,返回 NULL 值。

所有的簡單 CASE 表達式都可以替換爲等價的搜索 CASE 表達式。我們可以將上一節的示例改寫如下:

select emp_name as "員工姓名",
       case 
         when dept_id = 1 then '行政管理部'
         when dept_id = 2 then '人力資源部'
         when dept_id = 3 then '財務部'
         when dept_id = 4 then '研發部'
         when dept_id = 5 then '銷售部'
         when dept_id = 6 then '保衛部'
         else '其他部門'
       end as "所在部門"
from employee;

首先,判斷部門編號等於 1 是否成立(爲真),成立就顯示爲“行政管理部”;否則,判斷部門編號等於 2 是否成立, 成立就顯示爲“人力資源部”;依次類推;如果部門編號不等於 1 到 6 中的任何值,顯示爲“其他部門”。

以下查詢按照考試分數對成績進行評價:

select sname as "學生姓名",
       cname as "學科名稱",
       case 
         when score >= 90 then '優秀'
         when score >= 80 then '良好'
         when score >= 70 then '中等'
         when score >= 60 then '及格'
         else '不及格'
       end as "考試成績"
from t_case;
學生姓名|學科名稱|考試成績|
-------|------|-------|
張三   |語文   |良好    |
李四   |語文   |中等    |
王五   |語文   |優秀    |
張三   |數學   |良好    |
李四   |數學   |優秀    |
王五   |數學   |及格    |
張三   |英語   |良好    |
李四   |英語   |及格    |
王五   |英語   |良好    |

CASE 表達式除了可以用於 SELECT 列表,也可以出現在其他子句中,例如 WHERE、GROUP BY、ORDER BY 等。以下語句使用 CASE 表達式實現了第 10 篇 數據排序中的自定義排序:

select emp_name,
       case emp_name
         when '劉備' then 1
         when '關羽' then 2
         when '張飛' then 3
         else 99
       end as num
from employee
where dept_id = 1
order by case emp_name
           when '劉備' then 1
           when '關羽' then 2
           when '張飛' then 3
           else 99
         end;
 emp_name|num|
---------|---|
劉備      |  1|
關羽      |  2|
張飛      |  3|

通過 CASE 表達式將“劉備”編號爲 1,“關羽”編號爲 2,“張飛”編號爲 3,其他人員編號爲 99。

13.2 IF 函數

IF(expr1,expr2,expr3) 函數的處理邏輯如下:如果表達式 expr1 的結果爲 True,也就是不等於 0 也不爲 NULL,函數返回表達式 expr2 的值;否則,函數返回表達式 expr3 的值。

以下語句實現了與上文中 CASE 表達式相同的行轉列功能:

select sname,
       sum(if(cname = '語文', score, 0)) as "語文",
       sum(if(cname = '數學', score, 0)) as "數學",
       sum(if(cname = '英語', score, 0)) as "英語"
from t_case
group by sname;

以下語句將 bonus 爲空的數據顯示爲 0:

select emp_name, if(bonus, bonus, 0) as bonus
from employee;
emp_name |bonus   |
---------|--------|
劉備      |10000.00|
關羽      |10000.00|
張飛      |10000.00|
...
鄧芝      |       0|
簡雍      |       0|
孫乾      |       0|

13.3 IFNULL 函數

IFNULL(expr1,expr2) 函數的處理邏輯如下:如果表達式 expr1 不爲空,返回 expr1 的值;否則,返回表達式 expr2 的值。

以下語句同樣可以將 bonus 爲空的數據顯示爲 0:

select emp_name, ifnull(bonus, 0) as bonus
from employee;
emp_name |bonus   |
---------|--------|
劉備      |10000.00|
關羽      |10000.00|
張飛      |10000.00|
...
鄧芝      |       0|
簡雍      |       0|
孫乾      |       0|

13.4 COALESCE 函數

IFNULL 函數只能處理兩個參數,COALESCE(expr1,…) 函數可以返回參數中第一個非空的值,相當於嵌套的 IFNULL 函數。例如:

select coalesce(null, null, 1), coalesce(null, null, null);
coalesce(null, null, 1)|coalesce(null, null, null)|
-----------------------|--------------------------|
                      1|                          |

顯然,我們也可以使用 COALESCE 函數將 bonus 爲空的數據顯示爲 0。

13.5 NULLIF 函數

NULLIF(expr1,expr2) 函數的處理邏輯如下:如果表達式 expr1 和 expr2 相等,返回 NULL;否則,返回 expr1 的值。NULLIF 函數可以使用等價的 CASE 表達式進行表示:

CASE
  WHEN expr1 = expr2 THEN NULL
  ELSE expr1
END

NULLIF 函數的一個常見用途是防止除零錯誤:

-- 除零錯誤
select 1 / 0; 

select 1 / nullif(0 , 0);

第一個語句會產生除零錯誤(MySQL 可能只產生警告而不是錯誤);第二個語句返回 NULL。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章