# CASE

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

---

LLMS 索引： [llms.txt](/llms.txt)

---

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

## 本章要点 {#learn}

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

## 示例 {#example}

```sql {num="42.1" caption="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`。

## 运行示例 {#run}

运行方式：

```text
$ 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)
 ...
```

## 常见陷阱 {#pitfalls}

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

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

> [!NOTE] 动手练习
> 使用 `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 ...)`。

## 下载 {#downloads}

SQL 文件：[case.sql](/examples/source/case.sql) · [全部示例文件](/examples/examples.zip) · [准备工作](/#prerequisites)
