跳转到主要内容

42 CASE

使用 CASE 表达条件值,并比较使用 FILTER 的条件聚合。

CASE 是 SQL 的条件表达式——最接近 if/else 的结构。它有两种形式。搜索式 CASE(CASE WHEN cond THEN ... END)会计算任意布尔条件。简单 CASE(CASE expr WHEN value THEN ... END)会将一个表达式与特定值进行比较。只要允许使用表达式,就可以使用 CASE——包括 SELECT、WHERE、ORDER BY、GROUP BY,以及聚合函数内部的表达式。

本章要点

  • 用于一般条件的搜索式 CASE
  • 用于值相等判断的简单 CASE
  • ELSE 以及省略它时的结果
  • 使用 CASE 将行按组分桶
  • CASE 在条件聚合中的作用(以及 FILTER 何时可以替代它)

示例

示例 42.1 CASE
-- Searched CASE (most flexible)
SELECT id, name,
  CASE
    WHEN id <= 2  THEN 'low'
    WHEN id <= 4  THEN 'mid'
    ELSE              'high'
  END AS tier
FROM fruits
ORDER BY id
LIMIT 5;

-- Simple CASE (equality)
SELECT name,
  CASE name
    WHEN 'apricot' THEN 'A'
    WHEN 'blueberry' THEN 'B'
    ELSE '?'
  END AS code
FROM fruits
ORDER BY id
LIMIT 4;

-- CASE in WHERE
SELECT id, name
FROM fruits
WHERE CASE WHEN id < 5 THEN id > 1 ELSE false END
ORDER BY id;

-- CASE for bucketing and counting per bucket
SELECT
  CASE
    WHEN id <= 2 THEN 'low'
    WHEN id <= 4 THEN 'mid'
    ELSE              'high'
  END AS tier,
  count(*) AS cnt
FROM fruits
GROUP BY tier
ORDER BY tier;

-- Conditional aggregation: CASE inside an aggregate vs. FILTER (cleaner)
SELECT
  sum(CASE WHEN total > 30 THEN total ELSE 0 END) AS big_sum_old_style,
  sum(total) FILTER (WHERE total > 30)            AS big_sum_filter
FROM orders_example;

搜索式是主力形式。PostgreSQL 按从上到下的顺序计算条件,并返回第一个为真的条件所对应的结果。如果没有条件匹配且没有 ELSE,结果就是 NULL——有时有用,有时会造成错误。

简单形式是相等比较的简写:CASE name WHEN 'a' THEN ... END 与 CASE WHEN name = 'a' THEN ... END 等价。当需要将多个离散值映射到结果时,这种写法很简洁。

条件聚合——只统计或求和匹配的行——过去需要使用 sum(CASE WHEN cond THEN val ELSE 0 END)。sum(val) FILTER (WHERE cond) 可以直接表达这个条件。当存在输入行但没有任何行匹配时,它可能返回 NULL,而 sum(CASE WHEN cond THEN val ELSE 0 END) 会返回 0。如果希望空结果为 0,请使用 COALESCE。

运行示例

运行方式:

$ psql -f source/case.sql postgres
 id |    name    | tier
----+------------+------
  1 | apricot    | low
  2 | blueberry  | low
  3 | chestnut   | mid
  4 | damson     | mid
  5 | elderberry | high
(5 rows)
 ...

常见陷阱

  • 所有 THEN/ELSE 分支都必须具有共同类型——CASE WHEN ... THEN 1 ELSE 'x' END 会报错。混用类型时,请显式进行类型转换:CASE WHEN ... THEN 1::text ELSE 'x' END。
  • 忘记 END 是最常见的语法错误。
  • 不写 ELSE 时,如果没有分支匹配,结果就是 NULL。如果这让人意外,请添加显式的 ELSE ...。
  • 在 WHERE 中使用 CASE 虽然可行,但通常没有必要——大多数条件直接写成布尔表达式会更清楚。
提示

CASE 和 COALESCE 有重叠之处,但并不相同。对于“第一个不是 NULL 的值”,COALESCE(a, b, c) 写起来更短;当条件不只是“是否为 NULL”时,CASE 更合适。

动手练习

使用 CASE 将 id 映射为类似 ‘small’ / ‘medium’ / ’large’ 的标签,并按标签计数。在 ORDER BY 中使用 CASE,按自定义顺序排列优先级:ORDER BY CASE priority WHEN 'high' THEN 1 WHEN 'mid' THEN 2 ELSE 3 END。然后将 sum(CASE WHEN ...) 重写为 sum(...) FILTER (WHERE ...)。

下载

SQL 文件:case.sql · 全部示例文件 · 准备工作