使用Group By子句的时候,一定要记住下面的一些规则
- 不能Group By非标量基元类型的列,如不能Group By text,image或bit类型的列
- Select指定的每一列都应该出现在Group By子句中,除非对这一列使用了聚合函数
- 不能Group By在表中不存在的列;
- 进行分组前可以使用Where子句消除不满足条件的行
- 使用Group By子句返回的组没有特定的顺序,可以使用Order By子句指定次序
什么时候使用Group By?
可以考虑查询结果是以什么样的形式返回的
e.g1:求每个学生的所有科目中的最高分,显示学号,课程号,成绩
这个题应该用学号来进行分组吗?乍一看,我们需要对成绩表中所有学号相同的元组进行处理,好像需要用到"group by 学号"来进行分组。然而,本题也可以避免使用group by。不妨简单分析一下查询结果
可以看到,本题学号确实是逻辑上的"自成一组",但是另外两个属性列课程号,并不应该随着学号的分组而聚合。
select *
from cj as c1
where c1.成绩 >= all
(
select 成绩
from cj as c2
where c1.学号 = c2.学号
)
如果这一题改成只需要求学号和成绩,那么可以用group by来直接调用max聚合函数。
-- 变式
select 学号,max(成绩)
from cj
group by 学号
值得注意的是,这两条并不等价,如果某个学生的两门课均最大,前一个代码能够将两个均输出,而后一个不行
e.g2: 求每一年龄的学生人数
其查询结果应该包含若干行和一列,若干行分别对应着不同的年龄,一列为各个年龄的人数
结果按照年龄的不同分为若干个组,人数属性列需要按照不同的年龄进行聚合,因此需要使用group by按照年龄进行分组
select 年龄,count(*) as 人数
from xs
group by 年龄
e.g3: 求男同学的人数
其查询结果应该只有一行一列
结果只包含一个组,因此不需要使用 group by 来额外分组
select count(*) as 男同学人数
from xs
where 性别 = '男'
e.g4: 查询选课人数超过英语的选课人数的课程的课程号,课程名和人数
简单分析一下结果,首先,对于不同的课程,其选课人数一定是不同的。而成绩表中包含的是所有学生、课程的选课记录。因此我们必须通过按照课程号分组,聚合各门课程来求出各门课程的选课人数。其次,课程号与课程名是一一对应的,不应该对课程名进行聚合。诸如这种一部分属性应该聚合,另一部分属性不应该聚合的查询要求,我们应该先用子查询处理聚合的属性,然后与非聚合的属性进行连接。
select kc.课程号,kc.课程名,temp.人数
from kc right join
(
select 课程号,count(*) as 人数
from cj
group by 课程号
) as temp on kc.课程号 = temp.课程号
where temp.人数 >
(
select count(*)
from cj
where cj.课程号 =
(
select 课程号
from kc
where kc.课程名 = '英语'
)
)