随笔 - 99  文章 - 2 评论 - 902 trackbacks - 17
<2008年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789


转载请保留链接。
国家体育场(鸟巢)是建筑史上的令人惊叹之作,杂乱的基础结构组成了一个和谐的整体,正是中国社会的成功写照。 7-18 17:44

与我联系

常用链接

留言簿(12)

我参与的团队

我的标签

随笔分类

随笔档案

文章分类

相册

最新随笔

搜索

  •  

积分与排名

  • 积分 - 141508
  • 排名 - 243

最新评论

阅读排行榜

评论排行榜

60天内阅读排行

Sql Server2005中新增加了4个排名函数:ROW_NUMBER, RANK, DENSE_RANK, NTILE;大家一定已经对ROW_NUMBER非常熟悉了,所以我从最后一个NTILE开始分析。

NTILEmsdn中的解释是:将有序分区中的行分发到指定数目的组中。各个组有编号,编号从一开始。对于每一个行,NTILE 将返回此行所属的组的编号。不知道大家是不是一下子就能看懂这个解释,反正我是结合解释自己写了例子才弄明白的。

准备脚本,我们创建一个简单的3列表,三列分别是idcategoryId,和name,如下:

GO
if object_id('t_ntile','U'is not null
drop table t_ntile;
GO
create table t_ntile
(
    id 
int unique not null,
    categoryId 
int not null,
    name 
nvarchar(20)
)
go
INSERT INTO t_ntile VALUES(1,1,'A')
INSERT INTO t_ntile VALUES(2,4,'B')
INSERT INTO t_ntile VALUES(3,2,'C')
INSERT INTO t_ntile VALUES(4,1,'D')
INSERT INTO t_ntile VALUES(5,3,'E')
INSERT INTO t_ntile VALUES(6,3,'F')
INSERT INTO t_ntile VALUES(7,2,'G')
INSERT INTO t_ntile VALUES(8,2,'H')
INSERT INTO t_ntile VALUES(9,2,'I')
Go

查询语句如下:

SELECT id,categoryId,name
    ,
'ntile value' = NTILE(3OVER(PARTITION BY categoryId ORDER BY categoryId) 
FROM t_ntile

我们给NTITL传的参数是3,即表示一共三组,然后OVER中表达式指定要根据categoryId来分割分组,并要按照categoryId排序。上面的表达式执行结果如下:

 

----------------------------分割线-------------------------------

下面看RANKDENSE_RANK这对兄弟函数,这对函数要比NTITL容易理解一些。MSDNRANK的解释:返回结果集的分区内每行的排名。行的排名是相关行之前的排名数加一。MSDN上对DENSE_RANK的解释是:返回结果集分区中行的排名,在排名中没有任何间断。行的排名等于所讨论行之前的所有排名数加一。下面我用一个例子来说明一下,用结果说明他们的差别:

if object_id('student_class_grade','U'is not null
drop table student_class_grade;
GO
create table student_class_grade
(
    student_id 
int--学生id
    class_no int--班级编号
    grade int --成绩
);
GO
INSERT INTO student_class_grade VALUES(1,1,90);
INSERT INTO student_class_grade VALUES(2,1,85);
INSERT INTO student_class_grade VALUES(3,1,80);
INSERT INTO student_class_grade VALUES(4,1,80);
INSERT INTO student_class_grade VALUES(5,1,90);
INSERT INTO student_class_grade VALUES(6,1,75);
INSERT INTO student_class_grade VALUES(7,1,89);

INSERT INTO student_class_grade VALUES(11,2,90);
INSERT INTO student_class_grade VALUES(12,2,85);
INSERT INTO student_class_grade VALUES(13,2,80);
INSERT INTO student_class_grade VALUES(14,2,80);
INSERT INTO student_class_grade VALUES(15,2,90);
INSERT INTO student_class_grade VALUES(16,2,75);
INSERT INTO student_class_grade VALUES(17,2,89);
GO
--显示各个班级学生的成绩排名
SELECT student_id
    ,class_no,grade
    ,
'名次' = RANK() OVER(PARTITION BY class_no ORDER BY grade desc)
FROM student_class_grade
GO
SELECT student_id
    ,class_no,grade
    ,
'名次' = DENSE_RANK() OVER(PARTITION BY class_no ORDER BY grade desc)
FROM student_class_grade


分别执行下面两个select脚本,可以得到如下的结果

rank

可以看到1班同学的排名依次是1,1,3,4有了并列第一之后第二名的排序就是3了。

如下是DENSE_RANK的执行结果:

 dense_rank

可以看到排名依次是1,1,2,3 … 当出现两个并列第一之后,第二名的排名是2,而非RANK中的3.所以我们在给学生成绩排名时可以用DENSE_RANK而不是RANK

---------------------------分割线-------------------

最后要介绍的是ROW_NUMBER这个函数为我们分页提供了便利。我们可以结合CTE(通用表表达式)使用,如下例子

WITH CTE_rn (student_id,class_no,grade,rn) AS(
    
SELECT student_id,class_no,grade,rn = ROW_NUMBER() OVER(ORDER BY student_id ASC)
    
FROM student_class_grade
    
WHERE 0=0 --可以在此处加一些过滤条件,这样下面的分页的sql中就都不需要加条件了
)
--获得第-10条的数据
SELECT student_id,class_no,grade FROM CTE_rn WHERE rn BETWEEN 6 AND 10;
SELECT totalCn = COUNT(*FROM student_class_grade WHERE 0=0

ROW_NUMBER函数可以在取每个分类的前n条记录时很有用。
例如:
create table student(
id int not null,
name varchar(20),
grade int,
class int --班级
)
GO
--以下select语句返回每班级前3名的学生
WITH student_rn AS
(select id,name,class,grade, 排名 =ROW_NUMBER() OVER(PARTITION BY class ORDER BY grade DESC) FROM student)
SELECT id,name,class,grade, 排名 FROM student_rn
WHERE 排名 <= 3
全文结束。

Tag标签: sql server,t-sql
posted on 2008-04-28 08:59 玉开 阅读(2353) 评论(14)  编辑 收藏 所属分类: 数据库sql server

FeedBack:
#1楼  2008-04-28 09:04 李战      

  回复  引用  查看    
#2楼  2008-04-28 09:29 簡簡單單..      

  回复  引用  查看    
#3楼  2008-04-28 11:12 Kevin Zeng      
请教楼主 NTILE 这个的实际用途?
  回复  引用  查看    
#4楼 [楼主] 2008-04-28 12:22 玉开      
@Kevin Zeng
新生入校分宿舍,按照省份8个一组分

不知道这样说,你是否可以明白
  回复  引用  查看    
#5楼  2008-04-28 13:12 侯垒      
支持一下.
  回复  引用  查看    
#6楼  2008-04-28 14:14 lxhua [未注册用户]
支持!
  回复  引用    
#7楼  2008-04-28 14:58 Kevin Zeng      
--引用--------------------------------------------------
玉开: @Kevin Zeng
新生入校分宿舍,按照省份8个一组分

不知道这样说,你是否可以明白
--------------------------------------------------------

想了一会
明白了,呵呵。
  回复  引用  查看    
#8楼  2008-04-28 18:00 二嘎      
请教楼主
msdn:NTILE在msdn中的解释是:将有序分区中的行分发到指定数目的组中。各个组有编号,编号从一开始。对于每一个行,NTILE 将返回此行所属的组的编号。

也就是说所返回的值是组的编号,但是你的例子里有5个值为1的NTILE value
就是说第一组有5个值? 那为什么前三个分为一组?他们有何共性?对于你的解释我很不理解~

这是我的理解:我认为所传参数(3)并不代表3个一组,而是代表分组的总数目为3,就是说一共只能分为3组,数据库会按照一种算法一步步进行分配,本例中 第一次分配结果为1组categoryId1234 2组123 3组2
分配原则是1:不重复;2:按数字最全开始分配(例如第一组为1234)
第二次分配时从剩余数字里按categoryId最小的依次分配到三组里
由于剩余只剩2 所以分配到第一组中 这样结果为 1组 12234 2组123
3组2
假设我给你的原始数据增加几条记录
INSERT INTO t_ntile VALUES(10,6,'J')
INSERT INTO t_ntile VALUES(11,5,'K')
INSERT INTO t_ntile VALUES(12,5,'L')
INSERT INTO t_ntile VALUES(13,5,'M')
INSERT INTO t_ntile VALUES(14,5,'N')

按我的理解我的分配方案是(这是我没有验证过的,麻烦楼主验证)

第一次分配:categoryId:1组123456 2组1235 3组25
第二次分配:将剩余的2分配至1组 剩余的5分配至2组
最终结果为:1223456 12355 25
最后按categoryId后结果为
id categoryId ntile value
1 1 1
2 4 1
3 2 1
4 1 2
5 3 1
6 3 2
7 2 2
8 2 3
9 2 1
10 6 1
11 5 1
12 5 2
13 5 3
14 5 2

这是未按categoryId 排序的结果
  回复  引用  查看    
#9楼 [楼主] 2008-04-28 18:20 玉开      
@二嘎
我认为所传参数(3)并不代表3个一组,而是代表分组的总数目为3,就是说一共只能分为3组,数据库会按照一种算法一步
------------
你上面的这点肯定是正确的,我已经修改文章的内容;谢谢。后面的结果我有时间了验证。
  回复  引用  查看    
#10楼  2008-04-29 10:25 zdleek      
不错,mark
  回复  引用  查看    
#11楼  2008-05-06 12:24 沙加      
除了第三个RowNumber外,其它的都可以在程序里面做,我认为这种事情还是放到程序时面做好一些,除非要在存储过程里面使用.
  回复  引用  查看    
#12楼  2008-05-06 14:05 马可香蕉      
受教了,谢谢
  回复  引用  查看    
#13楼 [楼主] 2008-05-08 09:46 玉开      
补充,ROW_NUMBER函数可以在取每个分类的前n条记录时很有用。
例如:
create table student(
id int not null,
name varchar(20),
grade int,
class int --班级
)
GO
--以下select语句返回每班级前3名的学生
WITH student_rn AS
(select id,name,class,grade, 排名 =ROW_NUMBER() OVER(PARTITION BY class ORDER BY grade DESC) FROM student)
SELECT id,name,class,grade, 排名 FROM student_rn
WHERE 排名 <= 3


  回复  引用  查看    

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-05-15 12:45 编辑过
 
另存  打印