postgreSQL之开窗函数用法 – 记忆角落

postgreSQL之开窗函数用法

/ 1评 / 6

开窗函数语法相关

<窗口函数>
OVER (PARTITION BY <列清单> ORDER BY <排序用列清单>)

1.over 表示窗口函数关键字

2.partition by 对查询返回的结果集进行分组

3.order by 设定结果集的分组数据排序


可作为窗口函数的函数分类

1.聚合函数: SUM、AVG、COUNT、MAX、MIN

2.内置函数:RANK、DENSE_RANK、ROW_NUMBER 等专用窗口函数


avg() over()

演示avg()聚合函数后接over属性的窗口函数,此窗口函数用来计算分组后数据的平均值。

先创建一张成绩表

CREATE TABLE score_demo (
	id serial PRIMARY KEY,
	subject character varying(32),
	stu_name character varying(32),
	score numeric(3, 0)
);
INSERT INTO score_demo (subject, stu_name, score)
VALUES ('Chinese', 'francs', 70);
INSERT INTO score_demo (subject, stu_name, score)
VALUES ('Chinese', 'matiler', 70);
INSERT INTO score_demo (subject, stu_name, score)
VALUES ('Chinese', 'tutu', 80);
INSERT INTO score_demo (subject, stu_name, score)
VALUES ('English', 'matiler', 75);
INSERT INTO score_demo (subject, stu_name, score)
VALUES ('English', 'francs', 90);
INSERT INTO score_demo (subject, stu_name, score)
VALUES ('English', 'tutu', 60);
INSERT INTO score_demo (subject, stu_name, score)
VALUES ('Math', 'francs', 80);
INSERT INTO score_demo (subject, stu_name, score)
VALUES ('Math', 'matiler', 99);
INSERT INTO score_demo (subject, stu_name, score)
VALUES ('Math', 'tutu', 65);

查询每名学生学习成绩并且显示课程的平均分。请问我自己会怎么做呢?如果是不用窗口函数先做的话,我能想到是这样: 1.先计算出课程的平均分。 2.然后用成绩表与平均分表关联 如下:

select d.subject,d.stu_name,d.score,"temp".avg_score from score_demo d
LEFT JOIN (
	select subject,avg(score) avg_score from score_demo 
	group by subject
) temp on d.subject = temp.subject
-----
subject	stu_name	score	avg_score
Chinese	francs	70	73.3333333333333
Chinese	matiler	70	73.3333333333333
Chinese	tutu	80	73.3333333333333
English	matiler	75	75
English	francs	90	75
English	tutu	60	75
Math	francs	80	81.3333333333333
Math	matiler	99	81.3333333333333
Math	tutu	65	81.3333333333333

而使用窗口函数,基本上一句搞掂:

select subject,stu_name,score,avg(score)over(partition by subject) from score_demo

---
subject	stu_name	score	avg
Chinese	francs	70	73.3333333333333
Chinese	matiler	70	73.3333333333333
Chinese	tutu	80	73.3333333333333
English	matiler	75	75
English	francs	90	75
English	tutu	60	75
Math	francs	80	81.3333333333333
Math	matiler	99	81.3333333333333
Math	tutu	65	81.3333333333333

sum() over()

演示sum()聚合函数后接over属性的窗口函数,此窗口函数用来计算分组后数据的总分值

例如:查询每名学生学习成绩并且显示课程的总分

select subject,stu_name,score,sum(score)over(partition by subject) from score_demo

----

subject	stu_name	score	sum
Chinese	francs	70	220
Chinese	matiler	70	220
Chinese	tutu	80	220
English	matiler	75	225
English	francs	90	225
English	tutu	60	225
Math	francs	80	244
Math	matiler	99	244
Math	tutu	65	244

count() over()

演示count()聚合函数后接over属性的窗口函数,此窗口函数用来计算分组后数据的总条数值

select subject,stu_name,score,COUNT(score)over(partition by subject) from score_demo

---

subject	stu_name	score	count
Chinese	francs	70	3
Chinese	matiler	70	3
Chinese	tutu	80	3
English	matiler	75	3
English	francs	90	3
English	tutu	60	3
Math	francs	80	3
Math	matiler	99	3
Math	tutu	65	3

max() over()

演示max()聚合函数后接over属性的窗口函数,此窗口函数用来计算分组后数据的最大值

select subject,stu_name,score,MAX(score)over(partition by subject) from score_demo

---

subject	stu_name	score	max
Chinese	francs	70	80
Chinese	matiler	70	80
Chinese	tutu	80	80
English	matiler	75	90
English	francs	90	90
English	tutu	60	90
Math	francs	80	99
Math	matiler	99	99
Math	tutu	65	99

min() over()

演示min()聚合函数后接over属性的窗口函数,此窗口函数用来计算分组后数据的最小值

select subject,stu_name,score,min(score)over(partition by subject) from score_demo

---

subject	stu_name	score	min
Chinese	francs	70	70
Chinese	matiler	70	70
Chinese	tutu	80	70
English	matiler	75	60
English	francs	90	60
English	tutu	60	60
Math	francs	80	65
Math	matiler	99	65
Math	tutu	65	65

row_number()

row_number()窗口函数对结果集分组后的数据标注行号,从1开始的..

select row_number() over(partition by subject order by score desc),* from score_demo

----
row_number	id	subject	stu_name	score
1	3	Chinese	tutu	80
2	1	Chinese	francs	70
3	2	Chinese	matiler	70
1	5	English	francs	90
2	4	English	matiler	75
3	6	English	tutu	60
1	8	Math	matiler	99
2	7	Math	francs	80
3	9	Math	tutu	65

如果不指定partition属性,row_number()就类似于oracle的rownum记录所有行号。如:

select row_number() over(order by id) rownum,* from score_demo

----
1	1	Chinese	francs	70
2	2	Chinese	matiler	70
3	3	Chinese	tutu	80
4	4	English	matiler	75
5	5	English	francs	90
6	6	English	tutu	60
7	7	Math	francs	80
8	8	Math	matiler	99
9	9	Math	tutu	65

rank()

rank()窗口函数和row_number()函数类似, 主要区分为当组内某行字段值相同,行号重复并且行号产生间隙,如下:

select rank() over(partition by subject order by score) ,* from score_demo

-----

rank	id	subject	stu_name	score
1	2	Chinese	matiler	70
1	1	Chinese	francs	70
3	3	Chinese	tutu	80
1	6	English	tutu	60
2	4	English	matiler	75
3	5	English	francs	90
1	9	Math	tutu	65
2	7	Math	francs	80
3	8	Math	matiler	99

从返回结果看到..第一二行都是1,第三行就3了,产生了间隙...


dense_rank()

dese_rank()函数和rank()函数类似,主要区别为当组内某个字段值相同时,虽然行号重复,但行号不产生间隙。

select dense_rank() over(partition by subject order by score),* from score_demo

---

dense_rank	id	subject	stu_name	score
1	2	Chinese	matiler	70
1	1	Chinese	francs	70
2	3	Chinese	tutu	80
1	6	English	tutu	60
2	4	English	matiler	75
3	5	English	francs	90
1	9	Math	tutu	65
2	7	Math	francs	80
3	8	Math	matiler	99

percent_rank()

percent_rank()函数:当前行的相对排名: (rank- 1) / (总行数 - 1),得到的数在0-1之间[0,1), 例如:限制序号在0~1之间(0作为第一个序)-->[0,1):

select percent_rank() over(partition by subject order by score),* from score_demo

----

percent_rank	id	subject	stu_name	score
0	2	Chinese	matiler	70
0	1	Chinese	francs	70
1	3	Chinese	tutu	80
0	6	English	tutu	60
0.5	4	English	matiler	75
1	5	English	francs	90
0	9	Math	tutu	65
0.5	7	Math	francs	80
1	8	Math	matiler	99

cume_dist()

cume_dist()函数:当前行的相对排名: (当前行前面的行数 或 与当前行同等的行的行数)/(总行数)

注意:上面的percernt_rank()函数默认是从0开始排序的,如果需要使用相对0~1之间(0,1)的排名 例如,限制序号在0~1之间相对排名(0,1)

select cume_dist() over(partition by subject order by score),* from score_demo

---

cume_dist	id	subject	stu_name	score
0.666666666666667	2	Chinese	matiler	70
0.666666666666667	1	Chinese	francs	70
1	3	Chinese	tutu	80
0.333333333333333	6	English	tutu	60
0.666666666666667	4	English	matiler	75
1	5	English	francs	90
0.333333333333333	9	Math	tutu	65
0.666666666666667	7	Math	francs	80
1	8	Math	matiler	99

first_value()

first_value()函数用来取结果集每一个分组的第一行数的字段值。如:

select first_value(score) over(partition by subject ),* from score_demo

---

first_value	id	subject	stu_name	score
70	1	Chinese	francs	70
70	2	Chinese	matiler	70
70	3	Chinese	tutu	80
75	4	English	matiler	75
75	5	English	francs	90
75	6	English	tutu	60
80	7	Math	francs	80
80	8	Math	matiler	99
80	9	Math	tutu	65

所以通过first_value()函数很容易查询到分组数据的最大值和最小值。例如查询成绩表按课程分组同时取每门课程的最高分,如:

select first_value(score) over(partition by subject order by score desc) max_score,* from score_demo
----
max_score	id	subject	stu_name	score
80	3	Chinese	tutu	80
80	1	Chinese	francs	70
80	2	Chinese	matiler	70
90	5	English	francs	90
90	4	English	matiler	75
90	6	English	tutu	60
99	8	Math	matiler	99
99	7	Math	francs	80
99	9	Math	tutu	65

last_value()

last_value函数用来取结果集每一个分组的最后一行数据的字段值。 如:

select last_value(score) over(partition by subject ),* from score_demo
---
last_value	id	subject	stu_name	score
80	1	Chinese	francs	70
80	2	Chinese	matiler	70
80	3	Chinese	tutu	80
60	4	English	matiler	75
60	5	English	francs	90
60	6	English	tutu	60
65	7	Math	francs	80
65	8	Math	matiler	99
65	9	Math	tutu	65

nth_value()

nth_value()函数用来取结果集每一个分组的指定行数据的字段值,语法:

nth_value(value any,nth integer)

其中:
1.value 指定表的字段
2.nth 指定结果集分组数据的第几行,如不存在则返回空..

例如成绩表按课程分组后去分组的第三行分数,如:

select nth_value(score,3) over(partition by subject),* from score_demo

---
nth_value	id	subject	stu_name	score
80	1	Chinese	francs	70
80	2	Chinese	matiler	70
80	3	Chinese	tutu	80
60	4	English	matiler	75
60	5	English	francs	90
60	6	English	tutu	60
65	7	Math	francs	80
65	8	Math	matiler	99
65	9	Math	tutu	65

窗口函数别名的使用

如果sql中需要多次使用到窗口函数,可以使用窗口函数别名,语法如:

select .. from .. WINDOW window_name as (window_definiton)


WINDOW 属性指定表的别名为window_name,可以给over属性引用,如下:

select avg(score) over(r),sum(score) over(r) ,* from score_demo WINDOW r as (PARTITION by subject)

---

avg	sum	id	subject	stu_name	score
73.3333333333333	220	1	Chinese	francs	70
73.3333333333333	220	2	Chinese	matiler	70
73.3333333333333	220	3	Chinese	tutu	80
75	225	4	English	matiler	75
75	225	5	English	francs	90
75	225	6	English	tutu	60
81.3333333333333	244	7	Math	francs	80
81.3333333333333	244	8	Math	matiler	99
81.3333333333333	244	9	Math	tutu	65

好了终于写完常用的窗口函数了啊呜...要不打个赏?

 

《“postgreSQL之开窗函数用法”》 有 1 条评论

  1. repostone说道:

    非技术的路过。

发表评论

您的电子邮箱地址不会被公开。