SQL group by 语句注意事项

使用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。不妨简单分析一下查询结果

SQL 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: 求每一年龄的学生人数

其查询结果应该包含若干行和一列,若干行分别对应着不同的年龄,一列为各个年龄的人数

SQL group by 语句注意事项

结果按照年龄的不同分为若干个组,人数属性列需要按照不同的年龄进行聚合,因此需要使用group by按照年龄进行分组

select 年龄,count(*) as 人数
from xs
group by 年龄

e.g3: 求男同学的人数

其查询结果应该只有一行一列

SQL group by 语句注意事项

结果只包含一个组,因此不需要使用 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.课程名 = '英语'
		)
	)