跳转到主要内容

33 COUNT、SUM 与 AVG

统计数量、求和、求平均值并收集值,同时处理 NULL 和空输入。

聚合函数会对多行计算出一个值。最常见的五种——count、sum、avg、min、max——是基础函数。它们处理 NULL 的方式略有不同:count(*) 统计每一行,包括含 NULL 的行;count(col) 统计 col 的非 NULL 值;sum/avg/min/max 完全忽略 NULL。还有几十种其他函数(stddev、string_agg、array_agg、bool_and、bool_or、jsonb_agg……),每种聚合函数都有自己的行为:例如,array_agg 和 jsonb_agg 会保留 NULL 输入。

本章要点

  • count(*)、count(col) 与 count(DISTINCT col) 的区别
  • sum、avg、min、max 处理数值及其他类型
  • 聚合函数如何处理 NULL
  • 使用 string_agg 和 array_agg 构建列表
  • 使用 FILTER (WHERE ...) 为每个聚合设置条件

示例

示例 33.1 COUNT、SUM 与 AVG
-- Basic counts
SELECT count(*) AS total_rows FROM fruits;
SELECT count(name) AS non_null_names FROM fruits;
SELECT count(DISTINCT name) AS unique_names FROM fruits;

-- Sum and average
SELECT sum(id) AS sum_id, avg(id) AS avg_id, round(avg(id), 2) AS avg_round
FROM fruits;

-- Min and max work on numbers, text, dates
SELECT min(name) AS min_name, max(name) AS max_name FROM fruits;

-- Build a string or array from a column
SELECT string_agg(name, ', ' ORDER BY id) AS names_csv,
       array_agg(name ORDER BY id)         AS names_arr
FROM fruits;

-- FILTER: per-aggregate WHERE; replaces messy CASE WHEN tricks
SELECT count(*) AS total,
       count(*) FILTER (WHERE id <= 3) AS low,
       count(*) FILTER (WHERE id > 3)  AS high,
       sum(id)  FILTER (WHERE id > 3)  AS high_sum
FROM fruits;

count(*) 统计行数——快速而清晰。count(col) 会跳过 col 的 NULL 值;如果你想知道“有多少行的 email 非 NULL”,这就是正确的选择。count(DISTINCT col) 会先去重;因为必须先物化去重后的集合,所以更慢。

sum 返回总和;如果没有行则返回 NULL(不是 0——这很令人意外,但符合标准)。没有行时,avg 返回 NULL;对于整数输入,avg 返回 numeric,因为整数的平均值很少还是整数。min 和 max 适用于任何可排序的内容,包括文本和时间戳。

string_agg 和 array_agg 是 PostgreSQL 的“连接值”聚合函数——当你想生成列表时,它们正是合适的工具。聚合内部可选的 ORDER BY 控制连接顺序;默认的无序行为无法保证顺序。

FILTER (WHERE ...) 是 SQL 标准规定的、为单个聚合应用条件的方式。它取代了旧式的 sum(CASE WHEN cond THEN x END) 写法——结果相同,但清楚得多。

运行示例

运行方式:

$ psql -f source/count-sum-avg.sql postgres
 total_rows
------------
          8
(1 row)
 ...
   names_csv
-----------------------------------------------------------
 apricot, blueberry, chestnut, damson, elderberry, fruit_1, fruit_2, fruit_3
(1 row)

 total | low | high | high_sum
-------+-----+------+----------
     8 |   3 |    5 |      315
(1 row)

常见陷阱

  • 对零行求 sum 时,返回 NULL 而不是 0。如果需要 0,请使用 COALESCE(sum(x), 0)。
  • 整数列上的 avg 会产生 numeric 结果;如果想显示四舍五入后的值,可以把它包在 round 中。
  • count(col) 跳过 NULL 是一个特性,但会让初学者感到意外。count(*) 统计行数;其他形式都是“非 NULL 计数”。
  • 不使用 GROUP BY 的聚合会将整个结果聚合为一行。使用 GROUP BY 则每组得到一行(下一课介绍)。
提示

在一次扫描中组合聚合函数和 FILTER,即可同时得到许多统计结果。SELECT count(*), count(*) FILTER (WHERE is_active), avg(amount) FILTER (WHERE status = 'paid'), max(created_at) FROM ... 只扫描一次,却能给出许多答案。

动手练习

给 fruits 增加一个价格列:ALTER TABLE fruits ADD COLUMN price numeric(10,2);(然后用 UPDATE 修改一些价格),再对它运行 sum、avg、min、max。然后试试 SELECT string_agg(name, ', ' ORDER BY id DESC) FROM fruits;,看看顺序反过来了。

下载

SQL 文件:count-sum-avg.sql · 全部示例文件 · 准备工作