使用 row_number 获取实际返回行的行号

最近在做一个比较复杂的项目,其中用来排序的条件比较多,于是只能使用 row_number 函数进行排序,但是使用一次 row_number 之后得到的行号并不符合实际工作中的需要,需要根据这个排序之后的行号进行再次排序,于是麻烦就来了,需要类似以下的写法


declare @tb1 table(id int identity,cid int,sn nvarchar(150),tid int)
declare @tb2 table(sid int identity,coid int,stype int)
insert into @tb2(coid,stype) values(10,101),(14,101),(24,101),(31,101),(33,102),(63,102),(73,102),(83,102),(12,103),(22,103),(32,103),(42,103)
declare @i int
set @i = 0
while @i<10000
	begin
		insert into @tb1(cid,sn) values(convert(int,rand()*100),convert(varchar(50),newid()))
		set @i = @i + 1
	end
select * from (
	select ROW_NUMBER() over(order by ttid,stype desc,id desc) as rowid,* from (
		select ROW_NUMBER() over(partition by cid order by id desc) as ttid,* from @tb1 a left join @tb2 b on cid=b.coid
	) a
) a where rowid between 1 and 40 order by rowid

于是,我就在想,能不能在第一次row_number()的时候,同时进行第二次排序,于是在网络上查了半天,结果发现全都是用 row_number 进行排序,但 row_number 本身就是开窗函数,它无法进行嵌套,于是继续查找,然后发现可以使用 identity(int,1,1)来实现行号,但这个指令有一个巨大的缺陷,就是需要插入到临时表中

最后,没有办法,还是从 row_number 函数进行测试吧

经过考虑,row_number 函数只是进行了行号分配,如果没有多余的 order(over之外的)的话,排序默认按照 row_number 的结果来进行,如果有其他 order ,则 row_number 的结果不进行排序

于是,我就考虑,在使用 row_number 的同时,进行 order 排序,然后将排序后的结果从新分配行号

那么,怎么能够把当前集合分配上一个行号呢?

经过多次尝试,发现一个有趣的变量 @@identity,这个变量大家都知道,是用来记录最后一次insert之后的行号的,那么,按照这个变量进行排序的话,就不会打乱 order 的顺序,而是按照 order 的实际顺序进行排序,于是,上边的指令就可以变形一下

declare @tb1 table(id int identity,cid int,sn nvarchar(150),tid int)
declare @tb2 table(sid int identity,coid int,stype int)
insert into @tb2(coid,stype) values(10,101),(14,101),(24,101),(31,101),(33,102),(63,102),(73,102),(83,102),(12,103),(22,103),(32,103),(42,103)
declare @i int
set @i = 0
while @i<10000
	begin
		insert into @tb1(cid,sn) values(convert(int,rand()*100),convert(varchar(50),newid()))
		set @i = @i + 1
	end
select * from (
	select top 40
	row_number() over(partition by cid order by id desc) as rn1
	,row_number() over(order by @@identity) as rowid
	,* 
	from @tb1 a left join @tb2 b on a.cid=b.coid
	order by 1,stype desc,id desc
) a where rowid between 1 and 40 order by rowid

由于子查询不能在没有 top 的定义下使用 order,所以只能这么写了

其中 order by 1,就是按照第一个字段进行排序,哪怕它是开窗函数得到的结果也可以使用

需要注意的是,row_number() 函数按照 @@identity 排序的行号分配,要放到其他 row_number 之后,否则会在 order 之前就把行号分配好了,这样的结果是不符合我们的预期的

猜你喜欢

转载自blog.csdn.net/superwfei/article/details/51445901