教学文章
Technology Exchange
热门课程
400电话

免费咨询热线
400-090-9964

教学文章

PostgreSQL教程-查询-FROM子句-GROUP BY和HAVING子句

时间:2022-02-14 来源:

7.2.3. GROUP BY和HAVING子句

在通过了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是一个聚集函数,它在整个组上计算出一个单一值。有关可用的聚集函数的更多信息可以在第 9.20 节。

提示

没有聚集表达式的分组实际上计算了一个列中可区分值的集合。我们也可以用DISTINCT子句实现(参阅第 7.3.3 节)。

这里是另外一个例子:它计算每种产品的总销售额(而不是所有产品的总销售额):

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子句里, 因为它们都在查询的选择列表里被引用到(但见下文)。列s.units不必在GROUP BY列表里,因为它只是在一个聚集表达式(sum(...))里使用,它代表一组产品的销售额。对于每种产品,这个查询都返回一个该产品的所有销售额的总和行。

如果产品表被建立起来,例如product_id是主键,那么在上面的例子中用product_id来分组就够了,因为名称和价格都是函数依赖于产品ID,并且关于为每个产品ID分组返回哪个名称和价格值就不会有歧义。

在严格的 SQL 里,GROUP BY只能对源表的列进行分组,但PostgreSQL把这个扩展为也允许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 的组。请注意聚集表达式不需要在查询中的所有地方都一样。

如果一个查询包含聚集函数调用,但是没有GROUP BY子句,分组仍然会发生:结果是一个单一行(或者根本就没有行,如果该单一行被HAVING所消除)。它包含一个HAVING子句时也是这样,即使没有任何聚集函数调用或者GROUP BY子句。

版权所有@北京神脑资讯技术有限公司(CUUG,中国UNIX用户协会) Copyright ALL Rights Reserved 京ICP备11008061号-1

CUUG旗下网站:www.cuug.com.cn www.cuug.com oracle.cuug.com bbs.cuug.com www.cuug.net

电话:010-59426307 010-59426319 邮政编码:100089

地址:北京市海淀区北清路164号28-38号院