日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

SQL With As 用法Sql 四大排名函数(ROW_NUMBER、RANK、DENSE_RANK、NTILE)简介

發(fā)布時間:2025/7/14 数据库 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SQL With As 用法Sql 四大排名函数(ROW_NUMBER、RANK、DENSE_RANK、NTILE)简介 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Sql 四大排名函數(shù)(ROW_NUMBER、RANK、DENSE_RANK、NTILE)簡介

  排名函數(shù)是Sql Server2005新增的功能,下面簡單介紹一下他們各自的用法和區(qū)別。我們新建一張Order表并添加一些初始數(shù)據(jù)方便我們查看效果。

?

CREATE TABLE [dbo].[Order]([ID] [int] IDENTITY(1,1) NOT NULL,[UserId] [int] NOT NULL,[TotalPrice] [int] NOT NULL,[SubTime] [datetime] NOT NULL,CONSTRAINT [PK_Order] PRIMARY KEY CLUSTERED ([ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]GO SET IDENTITY_INSERT [dbo].[Order] ON GO INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (1, 1, 100, CAST(0x0000A419011D32AF AS DateTime)) GO INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (2, 2, 500, CAST(0x0000A419011D40BA AS DateTime)) GO INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (3, 3, 300, CAST(0x0000A419011D4641 AS DateTime)) GO INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (4, 2, 1000, CAST(0x0000A419011D4B72 AS DateTime)) GO INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (5, 1, 520, CAST(0x0000A419011D50F3 AS DateTime)) GO INSERT [dbo].[Order] ([ID], [UserId], [TotalPrice], [SubTime]) VALUES (6, 2, 2000, CAST(0x0000A419011E50C9 AS DateTime)) GO SET IDENTITY_INSERT [dbo].[Order] OFF GO ALTER TABLE [dbo].[Order] ADD CONSTRAINT [DF_Order_SubTime] DEFAULT (getdate()) FOR [SubTime] GO

?

?

  附上表結(jié)構(gòu)和初始數(shù)據(jù)圖:

  

?

一、ROW_NUMBER

  row_number的用途的非常廣泛,排序最好用他,一般可以用來實現(xiàn)web程序的分頁,他會為查詢出來的每一行記錄生成一個序號,依次排序且不會重復(fù),注意使用row_number函數(shù)時必須要用over子句選擇對某一列進行排序才能生成序號。row_number用法實例:

?

select ROW_NUMBER() OVER(order by [SubTime] desc) as row_num,* from [Order]

?

  查詢結(jié)果如下圖所示:

  

  圖中的row_num列就是row_number函數(shù)生成的序號列,其基本原理是先使用over子句中的排序語句對記錄進行排序,然后按照這個順序生成序號。over子句中的order by子句與SQL語句中的order by子句沒有任何關(guān)系,這兩處的order by 可以完全不同,如以下sql,over子句中根據(jù)SubTime降序排列,Sql語句中則按TotalPrice降序排列。

select ROW_NUMBER() OVER(order by [SubTime] desc) as row_num,* from [Order] order by [TotalPrice] desc

  查詢結(jié)果如下圖所示:

  

  利用row_number可以實現(xiàn)web程序的分頁,我們來查詢指定范圍的表數(shù)據(jù)。例:根據(jù)訂單提交時間倒序排列獲取第三至第五條數(shù)據(jù)。

with orderSection as (select ROW_NUMBER() OVER(order by [SubTime] desc) rownum,* from [Order] ) select * from [orderSection] where rownum between 3 and 5 order by [SubTime] desc

  查詢結(jié)果如下圖所示:

  

  注意:在使用row_number實現(xiàn)分頁時需要特別注意一點,over子句中的order by 要與Sql排序記錄中的order by 保持一致,否則得到的序號可能不是連續(xù)的。下面我們寫一個例子來證實這一點,將上面Sql語句中的排序字段由SubTime改為TotalPrice。另外提一下,對于帶有子查詢和CTE的查詢,子查詢和CTE查詢有序并不代表整個查詢有序,除非顯示指定了order by。

with orderSection as (select ROW_NUMBER() OVER(order by [SubTime] desc) rownum,* from [Order] ) select * from [orderSection] where rownum between 3 and 5 order by [TotalPrice] desc

  查詢結(jié)果如下圖所示:

  

  

二、RANK

  rank函數(shù)用于返回結(jié)果集的分區(qū)內(nèi)每行的排名,?行的排名是相關(guān)行之前的排名數(shù)加一。簡單來說rank函數(shù)就是對查詢出來的記錄進行排名,與row_number函數(shù)不同的是,rank函數(shù)考慮到了over子句中排序字段值相同的情況,如果使用rank函數(shù)來生成序號,over子句中排序字段值相同的序號是一樣的,后面字段值不相同的序號將跳過相同的排名號排下一個,也就是相關(guān)行之前的排名數(shù)加一,可以理解為根據(jù)當前的記錄數(shù)生成序號,后面的記錄依此類推。可能我描述的比較蒼白,理解起來也比較吃力,我們直接上代碼,rank函數(shù)的使用方法與row_number函數(shù)完全相同。

select RANK() OVER(order by [UserId]) as rank,* from [Order]

  查詢結(jié)果如下圖所示:

  

  由上圖可以看出,rank函數(shù)在進行排名時,同一組的序號是一樣的,而后面的則是根據(jù)當前的記錄數(shù)依次類推,圖中第一、二條記錄的用戶Id相同,所以他們的序號是一樣的,第三條記錄的序號則是3。  

?

三、DENSE_RANK

  dense_rank函數(shù)的功能與rank函數(shù)類似,dense_rank函數(shù)在生成序號時是連續(xù)的,而rank函數(shù)生成的序號有可能不連續(xù)。dense_rank函數(shù)出現(xiàn)相同排名時,將不跳過相同排名號,rank值緊接上一次的rank值。在各個分組內(nèi),rank()是跳躍排序,有兩個第一名時接下來就是第四名,dense_rank()是連續(xù)排序,有兩個第一名時仍然跟著第二名。將上面的Sql語句改由dense_rank函數(shù)來實現(xiàn)。

select DENSE_RANK() OVER(order by [UserId]) as den_rank,* from [Order]

  查詢結(jié)果如下圖所示:

  

  圖中第一、二條記錄的用戶Id相同,所以他們的序號是一樣的,第三條記錄的序號緊接上一個的序號,所以為2不為3,后面的依此類推。

四、NTILE

  ntile函數(shù)可以對序號進行分組處理,將有序分區(qū)中的行分發(fā)到指定數(shù)目的組中。?各個組有編號,編號從一開始。?對于每一個行,ntile 將返回此行所屬的組的編號。這就相當于將查詢出來的記錄集放到指定長度的數(shù)組中,每一個數(shù)組元素存放一定數(shù)量的記錄。ntile函數(shù)為每條記錄生成的序號就是這條記錄所有的數(shù)組元素的索引(從1開始)。也可以將每一個分配記錄的數(shù)組元素稱為“桶”。ntile函數(shù)有一個參數(shù),用來指定桶數(shù)。下面的SQL語句使用ntile函數(shù)對Order表進行了裝桶處理:

select NTILE(4) OVER(order by [SubTime] desc) as ntile,* from [Order]

  查詢結(jié)果如下圖所示:

  

  Order表的總記錄數(shù)是6條,而上面的Sql語句ntile函數(shù)指定的組數(shù)是4,那么Sql Server2005是怎么來決定每一組應(yīng)該分多少條記錄呢?這里我們就需要了解ntile函數(shù)的分組依據(jù)(約定)。

  ntile函數(shù)的分組依據(jù)(約定):

  1、每組的記錄數(shù)不能大于它上一組的記錄數(shù),即編號小的桶放的記錄數(shù)不能小于編號大的桶。也就是說,第1組中的記錄數(shù)只能大于等于第2組及以后各組中的記錄數(shù)。

  2、所有組中的記錄數(shù)要么都相同,要么從某一個記錄較少的組(命名為X)開始后面所有組的記錄數(shù)都與該組(X組)的記錄數(shù)相同。也就是說,如果有個組,前三組的記錄數(shù)都是9,而第四組的記錄數(shù)是8,那么第五組和第六組的記錄數(shù)也必須是8。

  這里對約定2進行詳細說明一下,以便于更好的理解。

  首先系統(tǒng)會去檢查能不能對所有滿足條件的記錄進行平均分組,若能則直接平均分配就完成分組了;若不能,則會先分出一個組,這個組分多少條記錄呢?就是 (總記錄數(shù)/總組數(shù))+1 條,之所以分配 (總記錄數(shù)/總組數(shù))+1 條是因為當不能進行平均分組時,總記錄數(shù)%總組數(shù)肯定是有余的,又因為分組約定1,所以先分出去的組需要+1條。

  分完之后系統(tǒng)會繼續(xù)去比較余下的記錄數(shù)和未分配的組數(shù)能不能進行平均分配,若能,則平均分配余下的記錄;若不能,則再分出去一組,這個組的記錄數(shù)也是(總記錄數(shù)/總組數(shù))+1條。

  然后系統(tǒng)繼續(xù)去比較余下的記錄數(shù)和未分配的組數(shù)能不能進行平均分配,若能,則平均分配余下的記錄;若還是不能,則再分配出去一組,繼續(xù)比較余下的......這樣一直進行下去,直至分組完成。

  舉個例子,將51條記錄分配成5組,51%5==1不能平均分配,則先分出去一組(51/5)+1=11條記錄,然后比較余下的 51-11=40 條記錄能否平均分配給未分配的4組,能平均分配,則剩下的4組,每組各40/4=10 條記錄,分配完成,分配結(jié)果為:11,10,10,10,10,曉菜鳥我開始就錯誤的以為他會分配成 11,11,11,11,7。

  根據(jù)上面的兩個約定,可以得出如下的算法:

?

//mod表示取余,div表示取整. if(記錄總數(shù) mod 桶數(shù)==0) {recordCount=記錄總數(shù) div 桶數(shù);//將每桶的記錄數(shù)都設(shè)為recordCount. } else {recordCount1=記錄總數(shù) div 桶數(shù)+1;int n=1;//n表示桶中記錄數(shù)為recordCount1的最大桶數(shù).m=recordCount1*n;while(((記錄總數(shù)-m) mod (桶數(shù)- n)) !=0){n++;m=recordCount1*n;}recordCount2=(記錄總數(shù)-m) div (桶數(shù)-n);//將前n個桶的記錄數(shù)設(shè)為recordCount1.//將n+1個至后面所有桶的記錄數(shù)設(shè)為recordCount2. }

?

?NTILE()函數(shù)算法實現(xiàn)代碼

  

  根據(jù)上面的算法,如果總記錄數(shù)為59,總組數(shù)為5,則 n=4 , recordCount1=12 , recordCount2=11,分組結(jié)果為 :12,12,12,12,11。

  如果總記錄數(shù)為53,總組數(shù)為5,則 n=3?, recordCount1=11 , recordCount2=10,分組結(jié)果為:11,11,11,10,10。

  就拿上面的例子來說,總記錄數(shù)為6,總組數(shù)為4,通過算法得到?n=2 , recordCount1=2 , recordCount2=1,分組結(jié)果為:2,2,1,1。

?

select ntile,COUNT([ID]) recordCount from (select NTILE(4) OVER(order by [SubTime] desc) as ntile,* from [Order] ) as t group by t.ntile

?

  運行Sql,分組結(jié)果如圖:

  

  比對算法與Sql Server的分組結(jié)果是一致的,說明算法沒錯。:)

?

總結(jié):

在使用排名函數(shù)的時候需要注意以下三點:

  1、排名函數(shù)必須有?OVER?子句。

  2、排名函數(shù)必須有包含 ORDER BY 的 OVER 子句。

  3、分組內(nèi)從1開始排序。

?

感謝:

  在博文的最后我要感謝園友?海岸線,他寫的?SQL2005四個排名函數(shù)(row_number、rank、dense_rank和ntile)的比較?對我?guī)椭艽?#xff0c;非常感謝!


感謝您懷著耐心看完整篇博文!!!
如果文章有什么錯誤或不當之處,請您斧正!
您有任何意見或者建議,您可以給我發(fā)郵件,也可以在下面留言,我看到了會第一時間回復(fù)您的,謝謝!
作者:曉菜鳥
出處:http://www.cnblogs.com/52XF/
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權(quán)利。

轉(zhuǎn)載于:https://www.cnblogs.com/Rvin/p/9375623.html

總結(jié)

以上是生活随笔為你收集整理的SQL With As 用法Sql 四大排名函数(ROW_NUMBER、RANK、DENSE_RANK、NTILE)简介的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。