前言:我一直十分喜欢使用SQL Server2005/2008的窗口函数,排名函数ROW_NUMBER()尤甚。今天晚上我在查看SQL Server开发的相关文档,整理收藏夹发现了两篇收藏已久的好文,后知后觉,读后又有点收获,顺便再总结一下。
一、从一个熟悉的示例说起
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
WITH Record AS (
SELECT
Row_Number() OVER ( ORDER BY Id DESC ) AS RecordNumber,
Id,
FirstName,
LastName,
Height,
Weight
FROM
Person (NOLOCK)
)
SELECT
RecordNumber,
( SELECT COUNT (0) FROM Record) AS TotalCount,
Id,
FirstName,
LastName,
Height,
Weight
FROM Record
WHERE RecordNumber BETWEEN 1 AND 10
|
二、窗口函数
1
2
3
4
5
6
7
8
|
CREATE TABLE [StudentScore](
[Id] [ int ] IDENTITY(1,1) NOT NULL ,
[StudentId] [ int ] NOT NULL CONSTRAINT [DF_StudentScore_StudentId] DEFAULT ((0)),
[ClassId] [ int ] NOT NULL CONSTRAINT [DF_StudentScore_ClassId] DEFAULT ((0)),
[CourseId] [ int ] NOT NULL CONSTRAINT [DF_StudentScore_CourseId] DEFAULT ((0)),
[Score] [ float ] NOT NULL CONSTRAINT [DF_StudentScore_Score] DEFAULT ((0)),
[CreateDate] [datetime] NOT NULL CONSTRAINT [DF_StudentScore_CreateDate] DEFAULT (getdate())
) ON [ PRIMARY ]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
--CourseId 2:语文 4:数学 8:英语 --1班学生成绩 INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (1,1,2,85)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (2,1,2,95.5)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (3,1,2,90)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (1,1,4,90)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (2,1,4,98)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (3,1,4,89)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (1,1,8,80)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (2,1,8,75.5)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (3,1,8,77)
--2班学生成绩 INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (1,2,2,90)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (2,2,2,77)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (3,2,2,78)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (4,2,2,83)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (1,2,4,98)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (2,2,4,95)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (3,2,4,78)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (4,2,4,100)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (1,2,8,85)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (2,2,8,90)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (3,2,8,86)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (4,2,8,78.5)
--3班学生成绩 INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (1,3,2,82)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (2,3,2,78)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (3,3,2,91)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (1,3,4,83)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (2,3,4,78)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (3,3,4,99)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (1,3,8,86)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (2,3,8,78)
INSERT INTO StudentScore(StudentId,ClassId,CourseId,Score) VALUES (3,3,8,97)
|
窗口函数使用OVER函数实现,OVER函数分带参和不带参两种。其中可选参数PARTITION BY用于将数据按照特定字段分组。
1
2
3
4
5
6
7
8
9
10
11
|
SELECT --Id,
--CreateDate,
StudentId,
ClassId,
CourseId,
Score,
CAST ( AVG (Score) OVER() AS decimal (5,2) ) AS '语文平均分'
FROM StudentScore
WHERE CourseId=2
|
1
2
3
4
5
6
7
8
9
10
11
|
SELECT Id,
CreateDate,
StudentId,
ClassId,
CourseId,
Score,
CAST ( AVG (Score) OVER(PARTITION BY ClassId ) AS decimal (5,2) ) AS '语文平均分'
FROM StudentScore
WHERE CourseId=2
|
b、可以在表达式中混合使用基本列和聚合列
二、让人爱不释手的排名函数
1
2
3
4
5
6
7
8
9
10
11
|
SELECT Id,
-- CreateDate, ROW_NUMBER() OVER( ORDER BY Score DESC ) AS '序号' ,
StudentId,
ClassId,
CourseId,
Score
FROM StudentScore
WHERE CourseId=8
|
1
2
3
4
5
6
7
8
9
10
11
|
SELECT Id,
-- CreateDate, RANK() OVER( ORDER BY Score DESC ) AS '序号' ,
StudentId,
ClassId,
CourseId,
Score
FROM StudentScore
WHERE CourseId=8
|
b、不同的是,ROW_NUMBER函数为每一个值生成唯一的序号,而RANK函数为相同的值生成相同的序号。
上图中,两个86分的学生对应的序号都是3,而接着排在它们下面的序号直接变成了5。
1
2
3
4
5
6
7
8
9
10
11
|
SELECT Id,
-- CreateDate, DENSE_RANK() OVER( ORDER BY Score DESC ) AS '序号' ,
StudentId,
ClassId,
CourseId,
Score
FROM StudentScore
WHERE CourseId=8
|
如果分区的行数不能被 integer_expression 整除,则将导致一个成员有两种大小不同的组。按照 OVER 子句指定的顺序,较大的组排在较小的组前面。
1
2
3
4
5
6
7
8
9
10
11
|
SELECT Id,
-- CreateDate, NTILE(6) OVER( ORDER BY ClassId DESC ) AS '组编号' ,
StudentId,
ClassId,
CourseId,
Score
FROM StudentScore
WHERE CourseId=8
|
参考文章: