在SQL Server中,窗体被定义为用户指定的一组行。

之所以要提出窗体这个概念,由于这种基于窗体或分区的又一次计算在实际工作应用范围比較广泛。比如。假设我们要对每一个班级中的学生按成绩进行排序,在对第1个班级排序完毕后,对第2个班级进行排序时编号须要又一次从1開始。在SQL Server 2005之前。像这种排序方式实现起来是比較烦琐的。能够说,对新窗体又一次启动计算是窗体计算的重要特点。

为支持窗体计算,SQLServer提供了OVER子句和窗体函数。

窗体函数在MSDN Library中被翻译为开窗函数。

尽管“开窗函数”理解起来并不如“窗体函数”easy,可是它描写叙述了数据窗体变化后又一次启动计算这样一个动作,所以我们尊重MSDN Library中的翻译。在兴许的介绍中将使用“开窗函数”这一名词。

窗体计算的两个主要应用就是对每组内的数据进行排序和聚合计算。因此,开窗函数也被分为排名开窗函数和聚合开窗函数。排名开窗函数如ROW_NUMBER( )、RANK( )。聚合开窗函数如AVG( )、SUM等。

进行排名计算时,OVER子句的语法格式例如以下:

OVER ( [ PARTITION BY value_expression , ... [ n ]]

<ORDER BY_Clause> )

PARTITION BY value_expression

指定对对应FROM子句生成的行集进行分区所根据的列。

开窗函数分别应用于每一个分区,并为每一个分区又一次启动计算。value_expression仅仅能引用通过FROM子句可用的列。不能引用选择列表中的表达式或别名。value_expression能够是列表达式、标量子查询、标量函数或用户定义的变量。

<ORDER BY 子句>

指定应用排名开窗函数的排序顺序。仅仅能引用通过FROM子句可用的列。可是不同通过指定整数来表示选择列表中列名称或列别名的位置。

以下我们将以表9-1所看到的的Students表为例,进行介绍。像Students表这种数据结构设计。相对于数据库存储而言是比較合理的,由于我们不可能为每一个班级创建一个表,但确实又存在像为每一个班级中的学生成绩进行排序或为学生编号这种实际需求。SQL Server的窗体计算技术就有效攻克了二者之间的矛盾。

从SQL Server2005開始,提供了4个排名函数。各自是:ROW_NUMBER( )、RANK( )、DENSE_RANK( )和NTILE( )。它们能够为分区中的每一行返回一个排名值。ROW_NUMBER( )用于按行进行编号,RANK( )和DENSE_RANK( )用于按指定顺序排名,NTILE( )用于对数据进行分区。

9.2.1 ROW_NUMBER( )

ROW_NUMBER( )返回分区内行的序列号,每一个分区的第一行从1開始。比如,以下的语句指定按ClassID进行分区。并按StudentName进行排序编号。查询结果如表9-2所看到的。

SELECT ClassID, StudentName, Achievement,

ROW_NUMBER() OVER(PARTITION BY ClassID ORDER BY StudentName) ASRowNumber

FROM Students;

表9-2                                                     按班级分区、按学生姓名进行编号

ClassID

StudentName

Achievement

RowNumber

1

Andrew

99.00

1

1

Grace

99.00

2

1

Janet

75.00

3

1

Margaret

89.00

4

2

Michael

72.00

1

2

Robert

91.00

2

2

Steven

86.00

3

3

Ann

94.00

1

3

Ina

80.00

2

3

Ken

92.00

3

3

Laura

75.00

4

为了理解SQL Server中排名函数的工作原理,我们来看一下查询优化器为查询生成的运行计划。如图9-1所看到的。

图9-1 为ROW_NUMBER( )生成的运行计划

由上图能够看出,为了计算排名。优化器首先按分区列排序。然后再对分区内行按ORDER BY子句指定的列排序。

假设事先为表创建了符合该排序条件的索引。则会直接扫描该索引文件。不再进行排序。

“序列射影”运算符的工作是负责计算排名。“段”运算符用于确定分组边界。

二者相互协调工作,来确定每一行的排名值。

“段”运算符在内存中会保留一行,用来与下一行的PARTITION BY列值进行比較。

对于表中的第一行。“段”运算符自然会发送true信号。对于后面的行,直到PARTITIONBY列值有变化之前,会一直发送false信号。

假设PARTITION BY列值发生了变化,说明已经到了下一个分区。“段”运算符会再次发送true信号。“序列射影”运算符在接收到true信号后,会重置排名值。

假设“序列射影”运算符接收到的是false信号,它会确认当前输入行的排序值是否不同于上一行。假设不同。则按排名函数所指示的递增排名值。自然,在该演示样例中,由于ROW_NUMBER( )函数须要为每一行递增值。因此。这个排序值比較步骤在该演示样例中是不存在的。可是。对于像RANK( )和DENSE_RANK( )函数。在运行计划中还会有另外一个“段”运算符,用于比較排序值是否有变化,以确定是否递增排名值。此问题我们在以下还会有介绍。

9.2.2 RANK( )和DENSE_RANK( )函数

ROW_NUMBER( )函数用于编号,它与排名具有不同的概念。比如,由表9-1能够看出,班级1中的Grace和Andrew的成绩同样。都是99分。假设使用ROW_NUMBER( )函数编号,有两种编号方案可供选择:一种是Grace第1、Andrew第2。还有一种是Andrew第1、Grace第2。这尽管都是正确的。它具有不确定性。

而排名则不同了。它具有确定性,同样的排序值总是被分配同样的排名值。Grace和Andrew在排名的情况下都应当是第1。也就是我们常说的并列第1。那他们两人之后的名次是什么呢?是第2还是第3呢?从两人并列第1的角度讲,他们两人之后的名次应当是第2。这也是DENSE_RANK()函数的排名方式。前面已经有2个人99分了,他们后面的人应当是第3个高分者,从这个角度理解。后面的名次应当是第3,这也是RANK( )的排名方式。

DENSE_RANK( )函数的排名方式我们称之为密集排名。由于它的名次之间没有间隔。

以下的语句演示了RANK()和DENSE_RANK( )的排名方式,查询结果如表9-3所看到的。

SELECT ClassID, StudentName, Achievement,

RANK() OVER(PARTITION BY ClassID ORDER BY Achievement DESC) AS SortRank,

DENSE_RANK() OVER(PARTITION BY ClassID ORDER BY Achievement DESC) AS SortDense

FROM Students;

表9-3                              按班级和考试成绩分别使用RANK( )和DENSE_RANK( )排名

ClassID

StudentName

Achievement

SortRank

SortDense

1

Grace

99.00

1

1

1

Andrew

99.00

1

1

1

Margaret

89.00

3

2

1

Janet

75.00

4

3

2

Robert

91.00

1

1

2

Steven

86.00

2

2

2

Michael

72.00

3

3

3

Ann

94.00

1

1

3

Ken

92.00

2

2

3

Ina

80.00

3

3

3

Laura

75.00

4

4

以下是为语句生成的运行计划。与ROW_NUMBER( )相比,运行计划中多出了一个“段”运算符。右边段的分组根据是ClassID,左边段的分组根据是ClassID和Achievement,这是多出的“段”。右边的“段”用于分区操作,在到达下一个分区时发送true信号,“序列射影”运算符会重置排名值。

而左边的“段”用于比較排序值是否有变化,假设有变化,则通知“序列射影”运算符递增排名值,递增方式则按RANK( )和DENSE_RANK( )函数的规则进行。

图9-2 为RANK( )和DENSE_RANK( )生成的运行计划

在SQL Server2005之前,也能够使用子查询的方式实现排名计算。

语句的原理就是查询出比当前成绩高的个数,再加上1,就是该成绩的排名。比如,在第1个班级中,比99分高的成绩为0。加上1后。该成绩就是第1名。以下语句的运行结果表9-3所看到的同样,可是由于对于每一个成绩都要运行两次子查询,在性能方面与RANK()和DENSE_RANK( )函数相差非常远。

SELECT ClassID, StudentName, Achievement,

(SELECT COUNT(*) FROM Students AS S2

WHERE S2.ClassID = S1.ClassID AND S2.Achievement > S1.Achievement)+1AS SortRank,

(SELECT COUNT(DISTINCT achievement) FROM Students AS S2

WHERE S2.ClassID = S1.ClassID AND S2.Achievement > S1.Achievement)+1AS SortDense

FROM Students AS S1

ORDER BY ClassID, Achievement DESC;

9.2.3 NTILE( )函数

NTILE( )函数用于把行分发到指定数目的组中。

各个组有编号。编号从1開始。对于每一个行,NTILE将返回此行所属的组的编号。

NTILE( )函数能够接受一个代表组数量的參数,分组的方式“均分”原则。比如,假设一个表有10行,须要分成2组。则每一个组都会有5行。假设表有11行,须要分成3个组。这时候是无法均分的。它分配方法是先得到一个能够整除的基组大小(11/3=3),每组应当分配3行。剩余的2行(11-9)会被再次均分到前面的2组中。

比如,以下的语句指定将Students表按学生成绩划分为3个组,而且Students表恰好也是11行。分组结果如表9-4所看到的。

SELECT ClassID, StudentName, Achievement,

NTILE(3) OVER(ORDER BY Achievement DESC) AS Tile

FROM Students;

表9-4                                                                           分组结果

ClassID

StudentName

Achievement

Tile

1

Grace

99.00

1

1

Andrew

99.00

1

3

Ann

94.00

1

3

Ken

92.00

1

2

Robert

91.00

2

1

Margaret

89.00

2

2

Steven

86.00

2

3

Ina

80.00

2

3

Laura

75.00

3

1

Janet

75.00

3

2

Michael

72.00

3

也能够先分区,再分组。比如,以下的语句将每一个班级的成绩划分为高、低两组。查询结果如表9-5所看到的。

能够看出,包括4名学生的班级,每组是2人;包括3名学生的班级,第1组是2人,第2组是1人。

SELECT ClassID, StudentName, Achievement,

CASENTILE(2) OVER(PARTITION BY ClassID ORDER BY Achievement DESC)

WHEN 1 THEN '高'

WHEN 2 THEN '低'

ENDAS Tile

FROM Students;

表9-5                                                         按班级分区再按成绩分组结果

ClassID

StudentName

Achievement

Tile

1

Grace

99.00

1

Andrew

99.00

1

Margaret

89.00

1

Janet

75.00

2

Robert

91.00

2

Steven

86.00

2

Michael

72.00

3

Ann

94.00

3

Ken

92.00

3

Ina

80.00

3

Laura

75.00

本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5356223.html,如需转载请自行联系原作者

瑞丽的SQL-基于窗体的排名计算相关推荐

  1. 5. 数据库题(以个人熟悉数据库为准、按要求写出sql) (1) 计算每个人的总成绩并排名(要求显示字段:学号,姓名,总成绩) (2) 计算每个人单科的最高成绩(要求显示字段: 学号,姓名,课程,最

    5. 数据库题(以个人熟悉数据库为准.按要求写出sql) (1) 计算每个人的总成绩并排名(要求显示字段:学号,姓名,总成绩) (2) 计算每个人单科的最高成绩(要求显示字段: 学号,姓名,课程,最高 ...

  2. 全文索引 排名计算问题

    排名计算问题 计算排名的过程,取决于一系列因素.不同语言的断字符对文本进行的词汇切分也不同.例如,字符串"dog-house"可以被一种断字符断为"dog"和& ...

  3. 基于Redis的微博计算好友关系

    基于Redis的微博计算好友关系 一.计算好友关系业务场景分析 微博微关系: 共同关注:是计算出阿甘和雷军共同关注的人有哪些? 我关注的人也关注他:是计算出我阿甘关注的人群中,有哪些人同时和我一样关注 ...

  4. 【R】【课程笔记】02+03 基于R软件的计算

    本文是课程<数据科学与金融计算>第2-3章的学习笔记,主要介绍R语言在统计和机器学习中的应用,用于知识点总结和代码练习,Q&A为问题及解决方案,参考书籍为<R软件及其在金融定 ...

  5. SQL 中的生成列/计算列以及主流数据库实现

    文章目录 什么是生成列? Oracle 中的虚拟列 MySQL 中的生成列 SQL Server 中的计算列 PostgreSQL 中的存储生成列 SQLite 中的生成列 什么是生成列? 在 SQL ...

  6. 阿里云基于Flink的流计算平台

    01 流计算开发运维痛点 1.1 任务需要底层API开发 1.2 任务逻辑调试 1.3 上下游数据预览 1.4 任务指标曲线 1.5 性能调优 1.6 监控报警 02 基于Flink的流计算平台 2. ...

  7. 基于图结构的计算分析和实现

    这两天调研了下基于图结构的计算方式,并以图结构的方式实现了简单的算式计算,具体过程如下文. 图结构构成 使用简单的将所有节点通过数组或链表进行管理起来 使用二维数组将节点之间的关系进行管理. 简单实现 ...

  8. 基于Coravel定时任务之计算总页数

    目录 基于Coravel定时任务之计算总页数 1 应用背景 2 对比各家定时库 2.1 TaskScheduler 2.2 Fluent Scheduler 2.3 Quartz.net 2.4 Ha ...

  9. 用python计算贷款_Python基于Logistic回归建模计算某银行在降低贷款拖欠率的数据示例...

    本文实例讲述了Python基于Logistic回归建模计算某银行在降低贷款拖欠率的数据.分享给大家供大家参考,具体如下: 一.Logistic回归模型: 二.Logistic回归建模步骤 1.根据分析 ...

最新文章

  1. 94.二叉树的中序遍历
  2. mysql mapper foreach_springboot结合MyBatis中使用foreach
  3. vue上传图片组件(支持拖拽文件夹上传)
  4. python获取键盘输入_python如何获取键盘输入
  5. Python 中的面向对象没有意义
  6. Android控件捕获点击事件的范围
  7. 系统简单的UIImagePickerController
  8. android win10 双系统,安卓+Win10双系统?这个可以有!
  9. 简述ip地址的abc类如何划分_ip地址的分类abc类的具体含义与分类方法
  10. 低代码平台集成方案,打通企业内部业务管理系统
  11. 数据安全--安全网关
  12. 【今年年会,你中奖了吗?】在线抽奖活动中如何实现中奖概率的自适应调整...
  13. win10 自带wi-fi热点速度太慢怎么办_win10wifi热点速度极慢如何解决
  14. 火盈云库浅谈新老版本的对比和发展过程
  15. Mybatis传递多个参数的4种方式(干货)
  16. 借势氢能源发展热潮,重塑股份持续加速行业布局
  17. FIX协议教程1-什么是FIX会话和管理消息
  18. RP产品原型资源分享-论坛类
  19. android人脸身份认证,Android通过支付宝进行刷脸认证
  20. 中国式“高定美学”燃爆广州秀场!「琢我」之气场与「莲玉芳华」之优雅

热门文章

  1. js内容混淆,返回内容加密
  2. 放弃51单片机直接学32是因为51难找工作?
  3. 很久以前看到的很经典的小说
  4. Unity基础(三)3D场景搭建
  5. 五分钟带你了解!Java程序的编译过程
  6. Python学期总结:从无知到入门
  7. linux系统查找dat文件命令,linux下的一些常用命令
  8. jquery学习笔记及常用函数封装
  9. 深度学习笔记(18)- 深度终端之一
  10. [转]通过 BT 种子 Hash 值从 BitComet 服务器上下载种子文件