(1)rank函數返回一個唯一的值,除非遇到相同的數據時,此時所有相同數據的排名是一樣的,同時會在最后一條相同記錄和下一條不同記錄的排名之間空出排名。
(2)dense_rank函數返回一個唯一的值,除非當碰到相同數據時,此時所有相同數據的排名都是一樣的。
(3)row_number函數返回一個唯一的值,當碰到相同數據時,排名按照記錄集中記錄的順序依次遞增。
(4)ntile是要把查詢得到的結果平均分為幾組,如果不平均則分給第一組。
例如:
create table s_score ( s_id number(6) ,score number(4,2) ); insert into s_score values(001,98); insert into s_score values(002,66.5); insert into s_score values(003,99); insert into s_score values(004,98); insert into s_score values(005,98); insert into s_score values(006,80); select s_id ,score ,rank() over(order by score desc) rank --按照成績排名,純排名 ,dense_rank() over(order by score desc) dense_rank --按照成績排名,相同成績排名一致 ,row_number() over(order by score desc) row_number --按照成績依次排名 ,ntile(3) over (order by score desc) group_s --按照分數划分成績梯隊 from s_score;
排名/排序的時候,有時候,我們會想到利用偽列row_num,利用row_num確實可以解決某些場景下的問題(但是相對也比較復雜),而且有些場景下的問題卻很難解決。
例:取成績前三名,並且前三名含有並列的情況。通過上面例子,我們可以直觀的看到,結果應該有5條記錄:
select s_id ,score ,dense_rank from ( select s_id ,score ,rank() over(order by score desc) rank ,dense_rank() over(order by score desc) dense_rank ,row_number() over(order by score desc) row_number from s_score ) t where dense_rank <= 3; S_ID SCORE DENSE_RANK ------- ------ ---------- 3 99.00 1 1 98.00 2 5 98.00 2 4 98.00 2 6 80.00 3
如果只是簡單的想到去用rownum <= 3 得到的結果顯然不可能是正確的。
組內的排名或者排序是經常遇到的一種場景。
例如,取每個銷售部門內,銷售業績最好的前三名。取每個班級內成績排名信息等等..
取每個班級內每門課成績排名第一的同學信息:
drop table S_SCORE; create table S_SCORE ( S_ID NUMBER(6), CLASS_ID VARCHAR2(2), COURSE VARCHAR2(20), SCORE NUMBER(5,2) ); INSERT INTO S_SCORE VALUES(1001,'A','MATH','67'); INSERT INTO S_SCORE VALUES(1004,'B','MATH','88'); INSERT INTO S_SCORE VALUES(1002,'A','MATH','99'); INSERT INTO S_SCORE VALUES(1003,'A','MATH','55'); INSERT INTO S_SCORE VALUES(1001,'B','MATH','88'); INSERT INTO S_SCORE VALUES(1001,'B','MATH','70'); INSERT INTO S_SCORE VALUES(1001,'A','ORACLE','97'); INSERT INTO S_SCORE VALUES(1004,'B','ORACLE','48'); INSERT INTO S_SCORE VALUES(1002,'A','ORACLE','79'); INSERT INTO S_SCORE VALUES(1003,'A','ORACLE','65'); INSERT INTO S_SCORE VALUES(1001,'B','ORACLE','82'); INSERT INTO S_SCORE VALUES(1001,'B','ORACLE','78'); select s_id ,class_id ,course ,score ,dense_rank() over (partition by class_id,course order by score desc) drk from S_SCORE; S_ID CLASS_ID COURSE SCORE DRK ------- -------- -------------------- ------- ---------- 1002 A MATH 99.00 1 1001 A MATH 67.00 2 1003 A MATH 55.00 3 1001 A ORACLE 97.00 1 1002 A ORACLE 79.00 2 1003 A ORACLE 65.00 3 1004 B MATH 88.00 1 1001 B MATH 88.00 1 1001 B MATH 70.00 2 1001 B ORACLE 82.00 1 1001 B ORACLE 78.00 2 1004 B ORACLE 48.00 3 select s_id ,class_id ,course ,score from ( select s_id ,class_id ,course ,score ,dense_rank() over (partition by class_id,course order by score desc) drk from S_SCORE ) t where drk = 1; S_ID CLASS_ID COURSE SCORE ------- -------- -------------------- ------- 1002 A MATH 99.00 1001 A ORACLE 97.00 1004 B MATH 88.00 1001 B MATH 88.00 1001 B ORACLE 82.00
rank()和dense_rank()用法相似,這里就不在舉例說明了。可以將上面的例子中dense_rank()替換成rank()實現。
接下來,看一個使用row_number()的場景
例:查看每個部門最近一筆銷售記錄:
select * from criss_sales order by dept_id,sale_date desc; DEPT_ID SALE_DATE GOODS_TYPE SALE_CNT ------- ----------- ---------- ----------- D01 2014/5/4 G02 80 D01 2014/4/30 G03 800 D01 2014/4/8 G01 200 D01 2014/3/4 G00 700 D02 2014/5/2 G03 900 D02 2014/4/27 G01 300 D02 2014/4/8 G02 100 D02 2014/3/6 G00 500
即,我們希望得到這兩條記錄:
D01 2014/5/4 G02 80 D02 2014/5/2 G03 900
select dept_id ,sale_date ,goods_type ,sale_cnt ,row_number() over (partition by dept_id order by sale_date desc) from criss_sales; DEPT_ID SALE_DATE GOODS_TYPE SALE_CNT ROW_NUMBER()OVER(PARTITIONBYDE ------- ----------- ---------- ----------- ------------------------------ D01 2014/5/4 G02 80 1 D01 2014/4/30 G03 800 2 D01 2014/4/8 G01 200 3 D01 2014/3/4 G00 700 4 D02 2014/5/2 G03 900 1 D02 2014/4/27 G01 300 2 D02 2014/4/8 G02 100 3 D02 2014/3/6 G00 500 4 select dept_id ,sale_date ,goods_type ,sale_cnt from ( select dept_id ,sale_date ,goods_type ,sale_cnt ,row_number() over (partition by dept_id order by sale_date desc) rn from criss_sales ) t where rn = 1; DEPT_ID SALE_DATE GOODS_TYPE SALE_CNT ------- ----------- ---------- ----------- D01 2014/5/4 G02 80 D02 2014/5/2 G03 900
有時會有這樣的需求:如果數據排序后分為三部分,業務人員只關心其中的一部分,如何將這中間的三分之一數據拿出來呢?
這時比較好的選擇,就是使用ntile函數:
select dept_id ,sale_date ,goods_type ,sale_cnt ,ntile(3) over (order by sale_cnt desc nulls last) all_cmp ,ntile(3) over (partition by dept_id order by sale_cnt desc nulls last) all_dept from criss_sales;
可以看到,Ntile函數為各個記錄在記錄集中的排名計算比例,返回每條記錄所在集合比例位置的值。
例如我們關心全公司前三分之一部分的數據,只需選擇 ALL_CMP = 1 的數據就可以了;
如果只是關心全公司中間的三分之一數據,只需選擇 ALL_CMP = 2 的數據就可以了。