PostgreSQL 18 中文参考文档(未翻译部分保留英文)🌐 切换到英文原版 →

2.7. 聚合函数 #

与绝大多数关系型数据库一致,PostgreSQL 提供了完善的聚合函数(aggregate functions)支持。聚合函数用于从多行输入数据中汇总计算出单个统计结果。例如,常见的聚合计算包括求行数(count)、求和(sum)、计算平均值(avg)、求最大值(max)以及求最小值(min)。

例如,通过以下语句查询记录中的最高最低气温:

SELECT max(temp_lo) FROM weather;

 max
-----
  46
(1 row)

如果我们想进一步知道这个最高最低气温发生在哪个(或哪些)城市,初学者可能会尝试这样写:

SELECT city FROM weather WHERE temp_lo = max(temp_lo);     -- 错误用法

但这将无法执行,因为聚合函数 max 不能直接出现在 WHERE 过滤子句中(这一限制源于底层的执行顺序:WHERE 子句用于在计算聚合之前筛选出哪些输入行进入统计池,因此它必须在聚合函数计算之前完成求值)。不过,通过嵌套一个独立的子查询(subquery),可以非常优雅地达成该目标:

SELECT city FROM weather
    WHERE temp_lo = (SELECT max(temp_lo) FROM weather);

     city
---------------
 San Francisco
(1 row)

该语句能够正确执行,因为内部子查询是一个独立的计算单元,它会先行算出最大值,随后作为常量提供给外部查询。

聚合函数与 GROUP BY 分组子句结合使用时功能极其强大。例如,按城市分组汇总各自的观测记录总数与最高最低气温:

SELECT city, count(*), max(temp_lo)
    FROM weather
    GROUP BY city;

     city      | count | max
---------------+-------+-----
 Hayward       |     1 |  37
 San Francisco |     2 |  46
(2 rows)

查询结果针对每个不同的城市聚合输出一行数据。如果需要对分组聚合后的结果进行进一步过滤,可以使用 HAVING 子句:

SELECT city, count(*), max(temp_lo)
    FROM weather
    GROUP BY city
    HAVING max(temp_lo) < 40;

  city   | count | max
---------+-------+-----
 Hayward |     1 |  37
(1 row)

这样就只保留了最高最低气温低于 40 度的城市分组。最后,如果我们只关心名称以字母 “S” 开头的城市:

SELECT city, count(*), max(temp_lo)
    FROM weather
    WHERE city LIKE 'S%'            -- (1)
    GROUP BY city;

     city      | count | max
---------------+-------+-----
 San Francisco |     2 |  46
(1 row)

(1)

LIKE 运算符用于执行模糊模式匹配,详见 Section 9.7。

深刻理解 SQL 中 WHERE 与 HAVING 两个子句与聚合函数之间的交互机制至关重要。两者的本质区别在于:WHERE 在分组与聚合计算之前筛选原始输入行(控制哪些原始数据参与统计);而 HAVING 则是在分组聚合完成之后对汇总行进行筛选。因此,WHERE 子句中绝对不能包含聚合函数;试图在筛选原始行阶段去引用聚合结果在逻辑上是矛盾的。相反,HAVING 子句几乎总是包含聚合函数(严格来说,在 HAVING 中编写不带聚合函数的普通字段条件是合法的,但这缺乏实用意义,因为普通条件完全可以在 WHERE 阶段更早、更高效地完成过滤)。

在前面的示例中,城市名过滤直接写在 WHERE 中,因为它无需依赖任何聚合计算。这比将其放在 HAVING 中执行效率高得多,因为这样可以提前剔除不相关的行,避免为它们浪费分组和聚合计算开销。

另一种精确控制哪些行进入特定聚合函数计算的方法是使用 FILTER 子句,它是专属于各个聚合函数的独立修饰选项:

SELECT city, count(*) FILTER (WHERE temp_lo < 45), max(temp_lo)
    FROM weather
    GROUP BY city;

     city      | count | max
---------------+-------+-----
 Hayward       |     1 |  37
 San Francisco |     1 |  46
(2 rows)

FILTER 的功能非常类似于 WHERE,区别在于它仅过滤附加在其后的单个聚合函数的输入数据池。在上述示例中,count 仅统计 temp_lo 小于 45 的记录行;但同行的 max 聚合函数仍然不受影响地作用于该分组的全部行,因此依然能准确计算出 46 的最大值。