4.10. 使用聚集函数

与其他大多数查询语言一样, PostgreSQL 支持聚集函数。 一个聚集函数从多个输入行计算单一一个结果。比如,有些聚集函数 对一个实例的集合进行 countsumavg (平均),max (最大值)和 min (最小值)的计算。

理解聚集和 SQL 的 WHEREHAVING 子句之间的相互作用是非常重要的。 在 WHEREHAVING 之间的基本差别是: WHERE 在分组和聚集计算之前选择输入行 (因此,它控制哪一行进入聚集计算), 而 HAVING 在分组和聚集计算之后选择分组行。 因此 WHERE 子句可能不包含聚集函数; 因为试图使用一个聚集来判断那些行需要输入到聚集函数中没有什么意义。 另一方面, HAVING 子句总是包含聚集函数。 (严格的说,你可以写一个不使用聚集的 HAVING 子句,不过是在浪费时间;相同的条件可以在 WHERE 阶段更有效地使用。)

例如,我们可以用下面语句找出最高的最低温度

SELECT max(temp_lo) FROM weather;
    
如我们想知道这个读数是在哪个城市发生的,我们可以
SELECT city FROM weather WHERE temp_lo = max(temp_lo);
    
不过这样做是不行的,因为聚集 max 不能在 WHERE中使用。不过,正如经常见到的那样, 我们可以重新排列查询以实现这样的目标;下面是通过运用 子查询实现的方式:
SELECT city FROM weather
    WHERE temp_lo = (SELECT max(temp_lo) FROM weather);
    
这样做是可以的,因为子查询是一个独立的计算, 它计算出与外层 select 无关的自身的聚集。

聚集在和 GROUP BY 子句联合使用时也是非常有用的。例如, 我们可以用下面语句获取在每个城市观察到的最低温度的最高值

SELECT city, max(temp_lo)
    FROM weather
    GROUP BY city;
    
这个查询为每个城市输出一行。我们可以用 HAVING 过滤这些分组的行:
SELECT city, max(temp_lo)
    FROM weather
    GROUP BY city
    HAVING min(temp_lo) < 0;
    
这个查询只给出那些最低温度有零下读数的城市。 最后,如果我们只关心以 "P"开头的城市,我们可以
SELECT city, max(temp_lo)
    FROM weather
    WHERE city like 'P%'
    GROUP BY city
    HAVING min(temp_lo) < 0;
    
请注意我们可以在 WHERE子句里面给出城市名称的约束,因为这是不需要聚集的。 这样比在 HAVING 上加限制更有效, 因为我们不必对未通过 WHERE 检查的行进行分组和聚集计算。