在通过了WHERE过滤器之后,生成的输入表可以继续用GROUP BY 子句进行分组,然后用HAVING子句选取一些分组行。
SELECT select_list
FROM ...
[WHERE ...]
GROUP BY grouping_column_reference [, grouping_column_reference]...
GROUP BY 子句 用于将一个表中所有列出的列值都相等的行分成一组。 列值列出的顺序没什么关系。 效果是将每个拥有相同值的行集合并为一组,代表该组中的所有行。 这样就可以删除输出里的重复,和/或计算应用于这些组的聚合。 比如:
=> SELECT * FROM test1; x | y ---+--- a | 3 c | 2 b | 5 a | 1 (4 rows) => SELECT x FROM test1 GROUP BY x; x --- a b c (3 rows)
在第二个查询里,我们不能写成SELECT * FROM test1 GROUP BY x, 因为字段y里没有哪个值可以和每个组关联起来。 被分组的列可以在选择列表中使用是因为它们每个组都有单一的数值。
通常,如果一个表被分了组,不在GROUP BY中列出的列不会出现在选择列表除非使用了聚合表达式。 一个带聚合表达式的例子是:
=> SELECT x, sum(y) FROM test1 GROUP BY x; x | sum ---+----- a | 4 b | 5 c | 2 (3 rows)
这里的sum是一个聚合函数,它在组上计算总和。 有关可用的聚合函数的更多信息可以在 聚合函数 中找到。
小技巧
没有有效的聚合表达式的分组可以计算一列中不同值的设置。 这个可以通过DISTINCT子句来实现(参考 DISTINCT )。
这里是另外一个例子:它计算每种产品的总销售额(而不是所有产品的总销售额)。
SELECT product_id, p.name, (sum(s.units) * p.price) AS sales
FROM products p LEFT JOIN sales s USING (product_id)
GROUP BY product_id, p.name, p.price;
在这个例子里,列product_id,p.name 和p.price必须在GROUP BY子句里, 因为它们都在查询选择列表里被引用了。(取决于products表是如何设计的,name和price可能完全依赖于产品ID,所以额外的分组理论上是不必要的,但是这仍未实现。)s.units列不必在 GROUP BY列表里,因为它只是在一个聚合表达式(sum(…))里使用, 它代表一组产品的销售总额。对于每种产品,这个查询都返回一个该产品的总销售额。
在严格的SQL里,GROUP BY只能对源表的列进行分组,但 WuTongDB 把这个扩展为也允许GROUP BY对选择列表中的列进行分组。也允许对值表达式进行分组,而不仅仅是简单的列。
如果一个表已经用GROUP BY分了组, 然后你又只对其中的某些组感兴趣,那么就可以用HAVING子句筛选分组。 必须像WHERE子句,从结果中消除组,语法是:
SELECT select_list FROM ... [WHERE ...] GROUP BY ... HAVING boolean_expression
在HAVING子句中的表达式可以引用分组的表达式和未分组的表达式 (后者必须涉及一个聚合函数)。
例子:
=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3; x | sum ---+----- a | 4 b | 5 (2 rows) => SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c'; x | sum ---+----- a | 4 b | 5 (2 rows)
然后是一个更现实的例子:
SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit
FROM products p LEFT JOIN sales s USING (product_id)
WHERE s.date > CURRENT_DATE - INTERVAL '4 weeks'
GROUP BY product_id, p.name, p.price, p.cost
HAVING sum(p.price * s.units) > 5000;
在上面的例子里,WHERE子句根据未分组的列选择数据行 (表达式只是对那些最近四周发生的销售为真)。而HAVING 子句在分组之后选择那些销售总额超过5000的组。 请注意聚合表达式不需要在查询中的所有地方都一样。




