您的位置:首页 > 数据库

PostgreSQL 分组集合新功能(GROUPING SETS,CUBE,ROLLUP)

2018-01-18 16:20 337 查看

PostgreSQL 分组集合新功能(GROUPING SETS,CUBE,ROLLUP)

实验环境

操作系统:windows 10 家庭中文版

数据库系统: PostgreSQL 9.6.2


说明

postgresql从9.5版本开始新加入了group by的分组集合功能,提供了GROUPING SETS,CUBE,ROLLUP参数,使用方式与oracle完全一致,下面是实际测试说明

构建测试环境

创建表t并插入测试数据:

create table t(id int,name varchar(20),class int,score int);

insert into t values(1,'math',1,90);
insert into t values(2,'math',2,80);
insert into t values(3,'math',1,70);
insert into t values(4,'chinese',2,60);
insert into t values(5,'chinese',1,50);
insert into t values(6,'chinese',2,60);
insert into t values(7,'physical',1,70);
insert into t values(8,'physical',2,80);
insert into t values(9,'physical',1,90);


结果:

test=# select * from t;
id |   name   | class | score
----+----------+-------+-------
1 | math     |     1 |    90
2 | math     |     2 |    80
3 | math     |     1 |    70
4 | chinese  |     2 |    60
5 | chinese  |     1 |    50
6 | chinese  |     2 |    60
7 | physical |     1 |    70
8 | physical |     2 |    80
9 | physical |     1 |    90
(9 行记录)


普通的group by

根据name和class字段求和:

test=# select name,class,sum(score)
test-# from t
test-# group by name,class
test-# order by name,class
test-# ;
name   | class | sum
----------+-------+-----
chinese  |     1 |  50
chinese  |     2 | 120
math     |     1 | 160
math     |     2 |  80
physical |     1 | 160
physical |     2 |  80
(6 行记录)


grouping set

GROUPING SETS的每个子列表可以指定零个或多个列或表达式,并且与其直接在GROUP BY子句中的解释方式相同。 一个空的分组集合意味着所有的行都被聚合到一个组中(即使没有输入行存在,也是输出)。

test=# select name,class,sum(score)
test-# from t
test-# group by grouping sets((name),(class),())
test-# order by name,class
test-# ;
name   | class | sum
----------+-------+-----
chinese  |       | 170
math     |       | 240
physical |       | 240
|     1 | 370
|     2 | 280
|       | 650
(6 行记录)


顺带一提,默认的group by语句相当于grouping set在grouping set后的参数填上所有group by的字段。如下:

test=# select name,class,sum(score)
test-# from t
test-# group by grouping sets((name,class))
test-# order by name,class
test-# ;
name   | class | sum
----------+-------+-----
chinese  |     1 |  50
chinese  |     2 | 120
math     |     1 | 160
math     |     2 |  80
physical |     1 | 160
physical |     2 |  80
(6 行记录)


与不使用grouping set语句时的结果完全相同

rollup

* rollup((a),(b),(c))等价于grouping sets((a,b,c),(a,b),(a),()) *

test=# select name,class,sum(score)
test-# from t
test-# group by rollup((name),(class))
test-# order by name,class
test-# ;
name   | class | sum
----------+-------+-----
chinese  |     1 |  50
chinese  |     2 | 120
chinese  |       | 170
math     |     1 | 160
math     |     2 |  80
math     |       | 240
physical |     1 | 160
physical |     2 |  80
physical |       | 240
|       | 650
(10 行记录)


等价于:

grouping sets((name,class),(name),())


cube

* cube((a),(b),(c))等价于grouping sets((a,b,c),(a,b),(a,c),(a),(b,c),(b),(c),()) *

test=# select name,class,sum(score)
test-# from t
test-# group by cube((name),(class))
test-# order by name,class
test-# ;
name   | class | sum
----------+-------+-----
chinese  |     1 |  50
chinese  |     2 | 120
chinese  |       | 170
math     |     1 | 160
math     |     2 |  80
math     |       | 240
physical |     1 | 160
physical |     2 |  80
physical |       | 240
|     1 | 370
|     2 | 280
|       | 650
(12 行记录)


等价于:

grouping sets((name,class),(name),(class),())


实际应用

我遇到一个需求,需要在分组统计总和之外附加所有组的总和,命名为total:

test=# select coalesce(name,'total') as name,
test-# coalesce(class,0) as class,
test-# coalesce(sum(score),0) as sum_score,
test-# coalesce(round(avg(score),2),0) as avg_score
test-# from t
test-# group by grouping sets((name,class),())
test-# order by name,class
test-# ;

name   | class | sum_score | avg_score
----------+-------+-----------+-----------
chinese  |     1 |        50 |     50.00
chinese  |     2 |       120 |     60.00
math     |     1 |       160 |     80.00
math     |     2 |        80 |     80.00
physical |     1 |       160 |     80.00
physical |     2 |        80 |     80.00
total    |     0 |       650 |     72.22
(7 行记录)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息