与绝大多数关系型数据库一致,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)
|
深刻理解 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 的最大值。