《Oracle 入门教程》第 09 篇 CASE 表达式

x33g5p2x  于2021-12-24 转载在 其他  
字(4.9k)|赞(0)|评价(0)|浏览(365)

Oracle 中的 CASE 表达式可以根据不同条件产生不同的结果,实现类似于编程语言中的 IF-THEN-ELSE 逻辑功能。例如,根据员工的 KPI 计算相应的涨薪幅度,根据考试成绩评出优秀、良好、及格等。

COALESCE 和 NULLIF 函数是两个简写形式的 CASE 表达式。另外,Oracle 还提供了专有的 NVL、NVL2 以及 DECODE 函数。

9.1 CASE 表达式

CASE 表达式支持两种形式:简单 CASE 表达式和搜索 CASE 表达式。

9.1.1 简单 CASE 表达式

简单 CASE 表达式的语法如下:

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

表达式的计算过程如下图所示:

首先计算 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 表达式。

9.1.2 搜索 CASE 表达式

搜索 CASE 表达式的语法如下:

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

表达式的计算过程如下图所示:

按照顺序依次计算每个分支中的条件(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 表达式实现了自定义的排序规则:

SELECT emp_name,
       CASE emp_name
         WHEN '刘备' THEN 1
         WHEN '关羽' THEN 2
         WHEN '张飞' THEN 3
         ELSE 99
       END AS num
FROM employee
ORDER BY CASE emp_name
           WHEN '刘备' THEN 1
           WHEN '关羽' THEN 2
           WHEN '张飞' THEN 3
           ELSE 99
         END;
EMP_NAME|NUM|
--------+---+
刘备     |  1|
关羽     |  2|
张飞     |  3|
孙丫鬟   | 99|
...
关平     | 99|

我们通过 CASE 表达式将“刘备”编号为 1,“关羽”编号为 2,“张飞”编号为 3,其他人员编号为 99。

9.2 COALESCE 函数

COALESCE(expr1, …) 函数返回参数中第一个非空的值,它的等价 CASE 表达式如下:

CASE
  WHEN expr1 IS NOT NULL THEN expr1
  WHEN expr2 IS NOT NULL THEN expr2
  WHEN expr3 IS NOT NULL THEN expr3
  ...
END

例如:

SELECT coalesce(NULL, NULL, 1), coalesce(NULL, NULL, NULL)
FROM dual;
COALESCE(NULL,NULL,1)|COALESCE(NULL,NULL,NULL)|
---------------------+------------------------+
                    1|                        |

9.3 NULLIF 函数

NULLIF(expr1,expr2) 函数的处理逻辑如下:如果表达式 expr1 和 expr2 相等,返回 NULL;否则,返回 expr1 的值。NULLIF 函数可以使用等价的 CASE 表达式进行表示:

CASE
  WHEN expr1 = expr2 THEN NULL
  ELSE expr1
END

NULLIF 函数的一个常见用途是防止除零错误:

-- 除零错误
SELECT 1 / 0
FROM dual;
SQL 错误 [1476] [22012]: ORA-01476: 除数为 0

SELECT 1 / NULLIF(0 , 0)
FROM dual;
1/NULLIF(0,0)|
-------------+
             |

第一个语句会产生除零错误,第二个语句返回 NULL。

9.4 NVL 函数

NVL(expr1, expr2) 函数,它用于返回第一个非空的参数值,相当于只有两个参数的 COALESCE 函数。

以下查询返回了员工的总收入:

SELECT emp_name,
       salary*12 + NVL(bonus, 0) AS "年收入"
FROM employee;
EMP_NAME|年收入 |
--------+------+
刘备     |370000|
关羽     |322000|
张飞     |298000|
诸葛亮   |296000|
黄忠     | 96000|
魏延     | 90000|
...

9.5 NVL2 函数

NVL2(expr1, expr2, expr3) 函数包含三个参数,如果第一个参数不为空,返回第二个参数的值;否则,返回第三个参数的值。以下查询同样返回员工的总收入:

SELECT emp_name,
       NVL2(bonus, salary*12 + bonus, salary*12) AS "年收入"
FROM employee;

9.6 DECODE 函数

DECODE 函数可以实现类似于简单 CASE 表达式的功能:

DECODE(expression, value1, result1, value2, result2, ...[, default_result ])

该函数依次比较表达式 expression 与 valueN 的值,如果找到相等的值就返回对应的 resultN;如果没有匹配到任何相等的值,返回默认结果 default_result;如果此时没有提供 default_result,返回 NULL 值。

以下语句利用 DECODE 函数将员工的部门编号显示为相应的名称:

SELECT emp_name,
       DECODE(dept_id, 1, '行政管理部',
                       2, '人力资源部',
                       3 ,'财务部',
                       4, '研发部',
                       5, '销售部',
                       6, '保卫部',
                          '其他部门') AS department
FROM employee;

该查询的结果与前文中的简单 CASE 表达式示例相同。DECODE 是 Oracle 专有函数,推荐大家使用标准的 CASE 表达式。

相关文章