使用 over 和 partition函数分组数据

使用 over 和 partition函数分组数据

除了group by, sql也可以使用 over 和partition by对结果集根据一定条件进行分组。本文解释如何结合这两个函数对数据进行分组展示。

准备数据

为了说明方便,我们定义表"student",有五列数据:id,name,age,gender,total_score.创建表语句为:

CREATE TABLE student
(
    id INT PRIMARY KEY IDENTITY,
    name VARCHAR(50) NOT NULL,
    gender VARCHAR(50) NOT NULL,
    age INT NOT NULL,
    total_score INT NOT NULL, 
 )

接着我们插入一些模拟数据:

INSERT INTO student 
VALUES ('Jolly', 'Female', 20, 500), 
('Jon', 'Male', 22, 545), 
('Sara', 'Female', 25, 600), 
('Laura', 'Female', 18, 400), 
('Alan', 'Male', 20, 500), 
('Kate', 'Female', 22, 500), 
('Joseph', 'Male', 18, 643), 
('Mice', 'Male', 23, 543), 
('Wise', 'Male', 21, 499), 
('Elis', 'Female', 27, 400);

下面开始了解问题。

需求

现在表中有10条记录,我们需要结果为id,name,gender,另外我们还需要属于每个性别的学生数,每个性别的平均年龄以及每个性别的总分。结果如下图:

IDNAMEGENDERTotal_studentsAverage_AgeTotal_Score
1JollyFemale5222400
3SaraFemale5222400
4LauraFemale5222400
6KateFemale5222400
10ElisFemale5222400
7JosephMale5202730
8MiceMale5202730
9WiseMale5202730
5AlanMale5202730
2JonMale5202730

我们看到,前三列包括每条记录的值,而后三列包括根据gender分组的聚集值。前五列中,Average_Age显示性别为Female的平均年龄,Total_Score显示性别为Female的总分和。总之结果集中既包括聚集值也包括非聚集值。

下面我们先获取聚集值,根据特定的列进行group by,使用group by 子句:

SELECT gender, count(gender) AS Total_Students, AVG(age) as Average_Age, SUM(total_score) as Total_Score
FROM student
GROUP BY gender

结果为:

GENDERTotal_studentsAverage_AgeTotal_Score
Female5222400
Male5202730

下面我们直接在分组语句中加入id 和 name (在select语句中直接加入非聚集列),看看是否可以得到期望的结果:

SELECT id, name, gender, count(gender) AS total_students, AVG(age) as Average_Age, SUM(total_score) as Total_Score
FROM student
GROUP BY gender

执行结果出错:

消息 8120,级别 16,状态 1,第 1 行
选择列表中的列 'student.id' 无效,因为该列没有包含在聚合函数或 GROUP BY 子句中。

错误指出id必须包括在group by子句中。显然这种方案行不通。

使用 JOIN 语句

既然我们已经得到了聚集数据,那么可以使用join实现聚集结果和非聚集结果合并。我们定义子查询返回根据gender分组结果,包括gender, Total_Students, Average_Age and the Total_Score 。然后和student表jion ,条件是gender。代码如下:

SELECT id, name, Aggregation.gender, Aggregation.Total_students, Aggregation.Average_Age, Aggregation.Total_Score
FROM student
INNER JOIN
(SELECT gender, count(gender) AS Total_students, AVG(age) AS Average_Age, SUM(total_score) AS Total_Score
FROM student
GROUP BY gender) AS Aggregation
on Aggregation.gender = student.gender

上面语句能够得到期望结果,但不是最佳方案,使用join和子查询增加代码复杂性,而且效率也不高(子查询产生临时表)。最佳方案将over 和partition by 子句集合使用。

使用over 和partition by

使用over 和partition by 子句,只需指定要对聚合结果进行分区的列,代码如下:

SELECT id, name, gender,
COUNT(gender) OVER (PARTITION BY gender) AS Total_students,
AVG(age) OVER (PARTITION BY gender) AS Average_Age,
SUM(total_score) OVER (PARTITION BY gender) AS Total_Score
FROM student

这种方法非常有效,非聚集列和聚集列一次性都返回了。

分组排名与排序

我们还可以利用over 和partition by进行分组排名和排序:

SELECT id, name, gender,
COUNT(gender) OVER (PARTITION BY gender) AS Total_students,
AVG(age) OVER (PARTITION BY gender) AS Average_Age,
SUM(total_score) OVER (PARTITION BY gender) AS Total_Score,
RANK() OVER (PARTITION BY gender ORDER BY total_score DESC) AS RANK_GENDER,
ROW_NUMBER() OVER (PARTITION BY gender ORDER BY total_score DESC) AS ROW_NUM
FROM student

执行结果:

IDNAMEGENDERTotal_studentsAverage_AgeTotal_ScoreRANK_GENDERROW_NUM
1JollyFemale522240011
3SaraFemale522240022
4LauraFemale522240023
6KateFemale522240044
10ElisFemale522240045
7JosephMale520273011
8MiceMale520273022
9WiseMale520273033
5AlanMale520273044
2JonMale520273055

我们看到排名自动跳过了重复名次。

总结

本文介绍了如何使用over 和partition by,与group by不同的是,其并没有合并记录,在一些场景中非常有用。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值