Kodokon kodokon.com

HAVING 与高级聚合

用 HAVING 在聚合之后进行筛选,并借助聚合函数内部的 CASE WHEN 对数据做透视。

8 分钟 · 3 题

在 Kodokon 中打开本课

你已经掌握了 GROUP BY。本模块要攻克的,正是区分一条正确查询和一条专业查询的东西:聚合之后的筛选、条件聚合,以及对求值顺序的扎实理解。想要练习,可以打开 sqliteonline.com(SQLite 引擎),或在终端里启动 sqlite3:每节课都会提供你所需要的 CREATE TABLEINSERT 语句。在运行示例之前,先复制这份数据集。

SQL
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  customer TEXT NOT NULL,
  category TEXT NOT NULL,
  amount REAL NOT NULL,
  status TEXT NOT NULL
);

INSERT INTO orders (customer, category, amount, status)
VALUES
  ('alice', 'books', 42.0, 'paid'),
  ('alice', 'games', 55.0, 'paid'),
  ('alice', 'books', 18.5, 'refunded'),
  ('bruno', 'games', 70.0, 'paid'),
  ('bruno', 'books', 12.0, 'paid'),
  ('chloe', 'games', 95.0, 'paid'),
  ('chloe', 'books', 30.0, 'paid'),
  ('david', 'books', 8.0, 'refunded');
本节课的数据集,请先运行它。

逻辑上的求值顺序是关键:FROMWHEREGROUP BYHAVINGSELECTORDER BYWHERE 在分组之前筛选HAVING 在聚合值算出之后筛选分组。这正是 WHERE SUM(amount) > 90 不合法的原因:在 WHERE 阶段,总和还根本不存在。这里,我们只保留已付款总额超过 90 的客户(预期结果:alice 和 chloe)。

SQL
SELECT customer,
       COUNT(*) AS nb_orders,
       SUM(amount) AS total
FROM orders
WHERE status = 'paid'
GROUP BY customer
HAVING SUM(amount) > 90;
WHERE 剔除行,HAVING 剔除分组。

第二个必知的模式是条件聚合。通过把 CASE WHEN 塞进 SUMCOUNT 内部,你只需扫描一遍表就能算出好几个指标,而新手则会跑三条各自独立的查询。这是把行透视成列的标准技巧。

SQL
SELECT customer,
  SUM(CASE WHEN category = 'books'
      THEN amount ELSE 0 END) AS books_total,
  SUM(CASE WHEN category = 'games'
      THEN amount ELSE 0 END) AS games_total
FROM orders
WHERE status = 'paid'
GROUP BY customer;
一次读取就把行透视成列。

知识检测

确认你已牢记本课的重点内容。

  1. 引擎按怎样的逻辑顺序对这些子句求值?
    • WHERE → GROUP BY → HAVING
    • HAVING → WHERE → GROUP BY
    • GROUP BY → HAVING → WHERE
  2. 哪个条件应当留在 WHERE 而不是 HAVING 中?
    • SUM(amount) > 100
    • status = 'paid'
    • COUNT(*) >= 2
  3. SUM(CASE WHEN category = 'books' THEN amount ELSE 0 END) 计算的是什么?
    • 所有订单的总额
    • 仅 'books' 类别那些行的总额
    • 'books' 类别的行数
    • 'books' 之外那些行的总额