SQL CASE 表達式


學習重點

  • CASE 表達式分為簡單 CASE 表達式和搜索 CASE 表達式兩種。搜索 CASE 表達式包含簡單 CASE 表達式的全部功能。

  • 雖然 CASE 表達式中的 ELSE 子句可以省略,但為了讓 SQL 語句更加容易理解,還是希望大家不要省略。

  • CASE 表達式中的 END 不能省略。

  • 使用 CASE 表達式能夠將 SELECT 語句的結果進行組合。

  • 雖然有些 DBMS 提供了各自特有的 CASE 表達式的簡化函數,例如 Oracle 中的 DECODE 和 MySQL 中的 IF,等等,但由於它們並非通用的函數,功能上也有些限制,因此有些場合無法使用。

一、什么是 CASE 表達式

本文將要學習的 CASE 表達式,和“1 + 1”或者“120 / 4”這樣的表達式一樣,是一種進行運算的功能。這就意味着 CASE 表達式也是函數的一種。它是 SQL 中數一數二的重要功能,希望大家能夠在這里好好學習掌握。

CASE 表達式是在區分情況時使用的,這種情況的區分在編程中通常稱為 (條件)分支 [1]

KEYWORD

  • CASE 表達式

  • 分支(條件分支)

二、CASE 表達式的語法

CASE 表達式的語法分為簡單 CASE 表達式搜索 CASE 表達式兩種。但是,由於搜索 CASE 表達式包含了簡單 CASE 表達式的全部功能,因此本文只會介紹搜索 CASE 表達式。想要了解簡單 CASE 表達式語法的讀者,可以參考本文末尾的“簡單 CASE 表達式”專欄。

KEYWORD

  • 簡單 CASE 表達式

  • 搜索 CASE 表達式

下面就讓我們趕快來學習一下搜索 CASE 表達式的語法吧。

語法 16 搜索 CASE 表達式

CASE WHEN <求值表達式> THEN <表達式>
     WHEN <求值表達式> THEN <表達式>
     WHEN <求值表達式> THEN <表達式>
       .
       .
       .
     ELSE <表達式>
END

WHEN 子句中的“<求值表達式>”就是類似“列 = 值”這樣,返回值為真值(TRUE/FALSE/UNKNOWN)的表達式。我們也可以將其看作使用 =!= 或者 LIKEBETWEEN 等謂詞編寫出來的表達式。

CASE 表達式會從對最初的 WHEN 子句中的“<求值表達式>”進行求值開始執行。所謂求值,就是要調查該表達式的真值是什么。如果結果為真(TRUE),那么就返回 THEN 子句中的表達式,CASE 表達式的執行到此為止。如果結果不為真,那么就跳轉到下一條 WHEN 子句的求值之中。如果直到最后的 WHEN 子句為止返回結果都不為真,那么就會返回 ELSE 中的表達式,執行終止。

KEYWORD

  • WHEN 子句

  • 求值

  • THEN 子句

  • ELSE

CASE 表達式名稱中的“表達式”我們也能看出來,上述這些整體構成了一個表達式。並且由於表達式最終會返回一個值,因此 CASE 表達式在 SQL 語句執行時,也會轉化為一個值。雖然使用分支眾多的 CASE 表達式編寫幾十行代碼的情況也並不少見,但是無論多么龐大的 CASE 表達式,最后也只會返回類似“1”或者“' 渡邊先生 '”這樣簡單的值。

三、CASE 表達式的使用方法

那么就讓我們來學習一下 CASE 表達式的具體使用方法吧。例如我們來考慮這樣一種情況,現在 Product(商品)表中包含衣服、辦公用品和廚房用具 3 種商品類型,請大家考慮一下怎樣才能夠得到如下結果。

A :衣服
B :辦公用品
C :廚房用具

因為表中的記錄並不包含“A :”或者“B :”這樣的字符串,所以需要在 SQL 中進行添加。我們可以使用 各種各樣的函數 中學過的字符串連接函數“||”來完成這項工作。

剩下的問題就是怎樣正確地將“A :”“B :”“C :”與記錄結合起來。這時就可以使用 CASE 表達式來實現了(代碼清單 41)。

代碼清單 41 通過 CASE 表達式將 A ~ C 的字符串加入到商品種類當中

SELECT product_name,
       CASE WHEN product_type = '衣服'
            THEN 'A:' || product_type
            WHEN product_type = '辦公用品'
            THEN 'B:' || product_type
            WHEN product_type = '廚房用具'
            THEN 'C:' || product_type
            ELSE NULL
       END AS abc_product_type
  FROM Product;

執行結果

 product_name | abc_product_type
--------------+------------------
 T恤衫        | A :衣服
 打孔器       | B :辦公用品
 運動T恤      | A :衣服
 菜刀         | C :廚房用具
 高壓鍋       | C :廚房用具
 叉子         | C :廚房用具
 擦菜板       | C :廚房用具
 圓珠筆       | B :辦公用品

6 行 CASE 表達式代碼最后只相當於 1 列(abc_product_type)而已,大家也許有點吃驚吧!與商品種類(product_type)的名稱相對應,CASE 表達式中包含了 3 條 WHEN 子句分支。最后的 ELSE NULL 是“上述情況之外時返回 NULL”的意思。ELSE 子句指定了應該如何處理不滿足 WHEN 子句中的條件的記錄,NULL 之外的其他值或者表達式也都可以寫在 ELSE 子句之中。但由於現在表中包含的商品種類只有 3 種,因此實際上有沒有 ELSE 子句都是一樣的。

KEYWORD

  • ELSE NULL

ELSE 子句也可以省略不寫,這時會被默認為 ELSE NULL。但為了防止有人漏讀,還是希望大家能夠顯式地寫出 ELSE 子句。

法則 3

雖然 CASE 表達式中的 ELSE 子句可以省略,但還是希望大家不要省略。

此外,CASE 表達式最后的“END”是不能省略的,請大家特別注意不要遺漏。忘記書寫 END 會發生語法錯誤,這也是初學時最容易犯的錯誤。

法則 4

CASE 表達式中的 END 不能省略。

  • CASE 表達式的書寫位置

    CASE 表達式的便利之處就在於它是一個表達式。之所以這么說,是因為表達式可以書寫在任意位置,也就是像“1 + 1”這樣寫在什么位置都可以的意思。例如,我們可以像下面這樣利用 CASE 表達式將 SELECT 語句的結果中的行和列進行互換。

    執行結果

    sum_price_clothes | sum_price_kitchen | sum_price_office
    ------------------+-------------------+-----------------
                5000 |             11180 |              600
    

    上述結果是根據商品種類計算出的銷售單價的合計值,通常我們將商品種類列作為 GROUP BY 子句的聚合鍵來使用,但是這樣得到的結果會以“行”的形式輸出,而無法以列的形式進行排列(代碼清單 42)。

    代碼清單 42 通常使用 GROUP BY 也無法實現行列轉換

    SELECT product_type,
        SUM(sale_price) AS sum_price
    FROM Product
    GROUP BY product_type;
    

    執行結果

    product_type | sum_price
    --------------+----------
    衣服         |      5000
    辦公用品     |       600
    廚房用具     |     11180
    

    我們可以像代碼清單 43 那樣在 SUM 函數中使用 CASE 表達式來獲得一個 3 列的結果。

    代碼清單 43 使用 CASE 表達式進行行列轉換

    -- 對按照商品種類計算出的銷售單價合計值進行行列轉換
    SELECT SUM(CASE WHEN product_type = '衣服'
                    THEN sale_price ELSE 0 END) AS sum_price_clothes,
        SUM(CASE WHEN product_type = '廚房用具'
                    THEN sale_price ELSE 0 END) AS sum_price_kitchen,
        SUM(CASE WHEN product_type = '辦公用品'
                    THEN sale_price ELSE 0 END) AS sum_price_office
    FROM Product;
    

    在滿足商品種類(product_type)為“衣服”或者“辦公用品”等特定值時,上述 CASE 表達式輸出該商品的銷售單價(sale_price),不滿足時輸出 0。對該結果進行匯總處理,就能夠得到特定商品種類的銷售單價合計值了。

    在對 SELECT 語句的結果進行編輯時,CASE 表達式能夠發揮較大作用。

專欄

簡單 CASE 表達式

CASE 表達式分為兩種,一種是本文學習的“搜索 CASE 表達式”,另一種就是其簡化形式——“簡單 CASE 表達式”。

簡單 CASE 表達式比搜索 CASE 表達式簡單,但是會受到條件的約束,因此通常情況下都會使用搜索 CASE 表達式。在此我們簡單介紹一下其語法結構。

簡單 CASE 表達式的語法如下所示。

語法 A 簡單 CASE 表達式

CASE <表達式>
   WHEN <表達式> THEN <表達式>
   WHEN <表達式> THEN <表達式>
   WHEN <表達式> THEN <表達式>
       .
       .
       .
   ELSE <表達式>
END

與搜索 CASE 表達式一樣,簡單 CASE 表達式也是從最初的 WHEN 子句開始進行,逐一判斷每個 WHEN 子句直到返回真值為止。此外,沒有能夠返回真值的 WHEN 子句時,也會返回 ELSE 子句指定的表達式。兩者的不同之處在於,簡單 CASE 表達式最初的“CASE <表達式>”也會作為求值的對象。

下面就讓我們來看一看搜索 CASE 表達式和簡單 CASE 表達式是如何實現相同含義的 SQL 語句的。將代碼清單 41 中的搜索 CASE 表達式的 SQL 改寫為簡單 CASE 表達式,結果如下所示(代碼清單 A)。

代碼清單 A 使用 CASE 表達式將字符串 A ~ C 添加到商品種類中

-- 使用搜索CASE表達式的情況(重寫代碼清單6-41)
SELECT product_name,
     CASE WHEN product_type = '衣服'
          THEN 'A :' | |product_type
          WHEN product_type = '辦公用品'
          THEN 'B :' | |product_type
          WHEN product_type = '廚房用具'
          THEN 'C :' | |product_type
          ELSE NULL
      END AS abc_product_type
 FROM Product;


-- 使用簡單CASE表達式的情況
SELECT product_name,
      CASE product_type
           WHEN '衣服'      THEN 'A :' || product_type
           WHEN '辦公用品'  THEN 'B :' || product_type
           WHEN '廚房用具'  THEN 'C :' || product_type
           ELSE NULL
       END AS abc_product_type
 FROM Product;

像“CASE product_type”這樣,簡單 CASE 表達式在將想要求值的表達式(這里是列)書寫過一次之后,就無需在之后的 WHEN 子句中重復書寫“product_type”了。雖然看上去簡化了書寫,但是想要在 WHEN 子句中指定不同列時,簡單 CASE 表達式就無能為力了。

專欄

特定的 CASE 表達式

由於 CASE 表達式是標准 SQL 所承認的功能,因此在任何 DBMS 中都可以執行。但是,有些 DBMS 還提供了一些特有的 CASE 表達式的簡化函數,例如 Oracle 中的 DECODE、MySQL 中的 IF 等。

KEYWORD

  • DECODE 函數(Oracle)

  • IF 函數(MySQL)

使用 Oracle 中的 DECODE 和 MySQL 中的 IF 將字符串 A ~ C 添加到商品種類(product_type)中的 SQL 語句請參考代碼清單 B。

代碼清單 B 使用 CASE 表達式的特定語句將字符串 A ~ C 添加到商品種類中

Oracle

-- Oracle中使用DECODE代替CASE表達式
SELECT  product_name,
      DECODE(product_type,
                 '衣服',      'A :' || product_type,
                 '辦公用品',  'B :' || product_type,
                 '廚房用具',  'C :' || product_type,
             NULL) AS abc_product_type
FROM Product;

MySQL

-- MySQL中使用IF代替CASE表達式
SELECT  product_name,
      IF( IF( IF(product_type = '衣服',
                  CONCAT('A :', product_type), NULL)
              IS NULL AND product_type = '辦公用品',
                  CONCAT('B :', product_type),
          IF(product_type = '衣服',
             CONCAT('A :', product_type), NULL))
                 IS NULL AND product_type = '廚房用具',
                    CONCAT('C :', product_type),
                 IF( IF(product_type = '衣服',
                      CONCAT('A :', product_type), NULL)
              IS NULL AND product_type = '辦公用品',
                 CONCAT('B :', product_type),
          IF(product_type = '衣服',
             CONCAT('A :', product_type),
        NULL))) AS abc_product_type
FROM Product;

但上述函數只能在特定的 DBMS 中使用,並且能夠使用的條件也沒有 CASE 表達式那么豐富,因此並沒有什么優勢。希望大家盡量不要使用這些特定的 SQL 語句。

請參閱

(完)


  1. 在 C 語言和 Java 等流行的編程語言中,通常都會使用 IF 語句或者 CASE 語句。CASE 表達式就是這些語句的 SQL 版本。 ↩︎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM