Oracle DB rownum & row_number() & rank() & dense_rank()

最近跟Oracle數據庫打了個小小的交道。有個表數據量太大。需要保留每個entry的最近10次結果,其餘的歷史結果全部刪掉。
因爲我們的場景很複雜,花了個把小時寫query,成功刪除了800多萬條數據。

刪好之後,覺得自己對於rownum, row_number, rank()等的理解並不深刻,於是谷歌了一番,仔細理解了下。現把自己的理解寫下來,供以後參考。

先把Oracle官方文檔貼出來:
Rownum https://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns009.htm
Row_number() https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions137.htm#i86310
Dense_rank()https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions043.htm
Rank()https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions123.htm

Rownum

Rownum的使用場景其實很有限。最常見就是top-N:
select * from xxtable where rownum < 10
如果想要order by某個列,在子句裏order by,在外層主句里加rownum的條件:
SELECT * FROM
(SELECT * FROM employees ORDER BY employee_id)
WHERE ROWNUM < 11;

因爲如果直接寫SELECT * FROM employees WHERE ROWNUM < 11 ORDER BY last_name; 的話,結果可能會跟預期不一樣。根據Oracle官方文檔,order by子句可能會喚起index取數,那它的前10條記錄,可能跟不走index取數的前10條記錄不一樣(我不是DB專家,還真是不太明白這塊)。總之,爲了能夠確切取到order by某個列的前N列,就應該用子句order by 外加主句 rownum條件。

如果rownum 條件寫 rownum > N,則一條數據都取不到。因爲取到的第一條數據rownum = 1, 它不符合 > N的條件,不會被返回。第二條數據就變成了第一條數據,它的rownum = 1,還是不會被返回。以此類推,沒有任何一條數據會被返回。

如果想要達到bottom N的效果,還是得用子句的方式來實現,在子句裏選出rownum這一列來,在主句里加條件:
select * from(
select rownum as rn, a.* from xxtable a where id = 20004 order by ref_Id desc)
where rn > 100

如果場景較複雜,需要聚合外加Top/Bottom/Inner條數限制,那麼我建議,聚合寫在子句裏,然後在主句里加rownum 限制。我試過將它們寫在一層,結果非常稀奇古怪,根本不是自己想要的。
select * from (
select parent_id, count(distinct scheduler_id) as amount from ref_column_mapping group by parent_id having count(distinct scheduler_id) > 5 order by amount desc)
where rownum < 4

Row_Number()

Row_Number()的話高級些,是個方法,可以基於某個partition進行進行編號,官方文檔給的例子就很能說明問題。每個partition從1開始編號,並且同一個partition內即便有相同記錄,也不會有相同的rownum.比如department id = 10 的partition裏,有兩個employee id = 199的記錄(當然不會發生,只是舉個例子),基於誰先取出來,誰的rownum就小一號,另一條大一號。
SELECT department_id, last_name, employee_id, ROW_NUMBER()
OVER (PARTITION BY department_id ORDER BY employee_id) AS emp_id
FROM employees;

DEPARTMENT_ID LAST_NAME EMPLOYEE_ID EMP_ID
10 Whalen 200 1
20 Hartstein 201 1
20 Fay 202 2
30 Raphaely 114 1
30 Khoo 115 2
30 Baida 116 3
30 Tobias 117 4
30 Himuro 118 5
30 Colmenares 119 6
40 Mavris 203 1
100 Popp 113 6
110 Higgins 205 1
110 Gietz 206 2

Rank() & Dense_Rank()

另外還有兩個方法,分別是rank() 和 dense_rank(),它們的作用是,在某個partition內,基於某個列(order by)進行排序,然後返回序號。它們倆的區別是:rank()排出來的序號可能是不連續的,比如1,2,2,2,5,6. Dense_rank()排出來的序號肯定是連續的,比如1,2,2,2,3,4.
Stack over flow上面有個帖子,裏面的例子非常的好,一目瞭然:https://stackoverflow.com/questions/11183572/whats-the-difference-between-rank-and-dense-rank-functions-in-oracle
with q as (
select 10 deptno, ‘rrr’ empname, 10000.00 sal from dual union all
select 11, ‘nnn’, 20000.00 from dual union all
select 11, ‘mmm’, 5000.00 from dual union all
select 12, ‘kkk’, 30000 from dual union all
select 10, ‘fff’, 40000 from dual union all
select 10, ‘ddd’, 40000 from dual union all
select 10, ‘bbb’, 50000 from dual union all
select 10, ‘xxx’, null from dual union all
select 10, ‘ccc’, 50000 from dual)
select empname, deptno, sal
, rank() over (partition by deptno order by sal nulls first) r
, dense_rank() over (partition by deptno order by sal nulls first) dr1
, dense_rank() over (partition by deptno order by sal nulls last) dr2
from q;

EMP DEPTNO SAL Rank Dr1 Dr2
xxx 10 1 1 4
rrr 10 10000 2 2 1
fff 10 40000 3 3 2
ddd 10 40000 3 3 2
ccc 10 50000 5 4 3
bbb 10 50000 5 4 3
mmm 11 5000 1 1 1
nnn 11 20000 2 2 2
kkk 12 30000 1 1 1

9 rows selected.

Rank() & Dense_rank() 與Row_number()的區別在於: 某個partition內,前者的序號可能重複,後者不可能重複(即便order by 某個列之後有兩行記錄的這個字段是一樣的)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章