锋利的SQL:从分组中取前几行数据(一)

2014-11-24 09:58:20 · 作者: · 浏览: 3

锋利的SQL:从分组中取前几行数据
这是在做一个考试成绩统计时遇到的问题。假设有如表1所示的数据,其中包含了3个班级的考生成绩,如果是希望获取全部数据的前2名,可以使用TOP配合ORDER BY子句轻易实现,但是如果我们希望取出每个班级中的前2名呢?事情就不这么简单了。
www.2cto.com
SELECT TOP(2) *
FROM Students
ORDER BY Achi DESC;
表1 考试成绩表
ClassID
StuName
Achi
1
张山
100
1
李明
90
1
王磊
95
2
孙科
100
2
赵强
80
2
王智
90
3
李海
95
下面的语句用于创建示例表:
CREATE TABLE Students
(ClassID int,
StuNamechar(10),
Achi int);
INSERT INTO Students
VALUES(1, '张山', 100),
(1, '李明', 90),
(1, '王磊', 95),
(2, '孙科', 100),
(2, '赵强', 80),
(2, '王智', 90),
(3, '李海', 95);
1.使用联接获取前几行
如果将Students表打开两次,将一个考生与其大于或等于自己成绩的考生联接,我们看看会得到什么样的结果。参考下面的语句:
SELECT S1.*, S2.*
FROM Students AS S1
INNER JOINStudents AS S2
ONS1.ClassID = S2.ClassID
ANDS2.Achi >= S1.Achi
ORDER BY S1.ClassID, S1.Achi DESC;
www.2cto.com
结果如表2所示。
表2 联接结果
S1.ClassID
S1.StuName
S1.Achi
S2.ClassID
S2.StuName
S2.Achi
1
张山
100
1
张山
100
1
王磊
95
1
张山
100
1
王磊
95
1
王磊
95
1
李明
90
1
张山
100
1
李明
90
1
李明
90
1
李明
90
1
王磊
95
2
孙科
100
2
孙科
100
2
王智
90
2
孙科
100
2
王智
90
2
王智
90
2
赵强
80
2
孙科
100
2
赵强
80
2
赵强
80
2
赵强
80
2
王智
90
3
李海
95
3
李海
95
从上表中可以看出,1班中的第1名张山有1条记录,第2名王磊有2条记录,第3名有3条记录。因此,我们可以使用下面的语句来获取每班中前2名的考生。查询结果如表3所示。
www.2cto.com
SELECT S1.ClassID, S1.Achi, MAX(S1.StuName) ASStuName
FROM Students AS S1
INNER JOINStudents AS S2
ONS1.ClassID = S2.ClassID
ANDS2.Achi >= S1.Achi
GROUP BY S1.ClassID, S1.Achi
HAVING COUNT(*) <=2
ORDER BY S1.ClassID, S1.Achi DESC;
表3 每班中前2名的考生
ClassID
Achi
StuName
1
100
张山
1
95
王磊
2
100
孙科
2
90
王智
3
95
李海
2.使用窗口排名函数获取前几行
窗口计算是从SQLServer 2005开始提供的新技术,每一组数据被称为一个窗口,RANK( )和DENSE_RANK( )函数都可以按窗口进行排名计算,表4描述了这两种排名方式的差异。
表4 RANK( )和DENSE_RANK( )函数排名的差异
StuName
Achi
RANK( )排名
DENSE_RANK( )排名
张三
100
1
1
李四
100
1
1
王五
95
3
2
赵六
90
4
3
从表中可以看出,无论是RANK()还是DENSE_RANK(),相同的考试成绩排名值是相同的,张三和李四都是第1,也就是我们常说的并列第1。但是,两人之后