Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

2.7. 聚合函数 #

像其他大多数关系数据库产品一样,PostgreSQL 支持聚合函数。聚合函数从多个输入行计算出一个单一结果。例如,对于一组行,有用于计算countsumavg(平均值)、max(最大值)和min(最小值)的聚合函数。

作为示例,我们可以使用以下方法查找任意位置的最高低温读数

SELECT max(temp_lo) FROM weather;
 max
-----
  46
(1 row)

如果我们想知道该读数出现在什么城市(或哪些城市),我们可能会尝试

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

但这行不通,因为聚合max不能用于WHERE子句。(存在此限制是因为WHERE子句决定了哪些行将包括在聚合计算中;因此,显然需要在计算聚合函数之前对其进行求值。)但是,通常情况下,可以重新表述该查询以实现所需的结果,这里通过使用子查询

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)

这为我们提供了相同的结果,仅针对所有temp_lo值均低于 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运算符执行模式匹配,在第 9.7 节中有说明。

了解聚合与SQLWHEREHAVING 子句。核心区别在于 WHEREHAVINGWHERE 在组计算和聚合之前选择输入行(因此,它控制哪些行进入聚合计算),而 HAVING 在组计算和聚合之后选择组行。因此,WHERE 子句不能包含聚合功能;用聚合来确定哪些行会作为聚合输入项毫无意义。另一方面,HAVING 子句总是包含聚合功能。(严格来说,您可以编写不使用聚合的 HAVING 子句,但它很少有用。同样的条件可以更有效地在 WHERE 阶段使用。)

在前一个示例中,我们可以在 WHERE 中应用城市名称限制,因为它不需要聚合。这比将限制添加到 HAVING 更有效,因为我们避免了对未通过 WHERE 检查的所有行进行分组和聚合计算。

用于选择适用于聚合计算的行的一种方法是使用 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。