Oracle中rownum&rowid

做過Oracle分頁的人都知道由於Oracle中沒有像MySql中limit函數以及SQLServer中的top關鍵字等,所以只能通過僞列的方式去滿足分頁功能,在此,不談分頁方法,只從根本上去介紹這兩個僞列的用法與原理

rownum和rowid都是僞列,但是兩者的根本是不同的

  • rownum是根據sql查詢出的結果給每行分配一個邏輯編號,所以你的sql不同也就會導致最終rownum不同
  • rowid是物理結構上的,在每條記錄insert到數據庫中時,都會有一個唯一的物理記錄 (不會變)

    下面以scott用戶的emp表爲例.

一、ROWNUM僞列

ROWNUM僞列是Oracle首先進行查詢獲取到結果集之後在加上去的一個僞列,這個僞列對符合條件的結果添加一個從1開始的序列號,先看一個例子:

SELECT ROWNUM,empno,ename,job FROM emp WHERE deptno = 30;

結果如下:


可以看到確實添加了一列從1開始的序列號,那麼有了這個僞列,就可以完成好多提取數據的工作,比如提取emp表中前5條數據,SQL如下:

SELECT ROWNUM,empno,ename,job FROM emp WHERE ROWNUM < 6;

ROWNUM是動態的,也就是必須先有查詢到的結果集,然後再給這個結果集加上一個列,比如給結果集中的第一條數據的ROWNUM的值爲1,依次類推,如果此時這樣寫:

SELECT ROWNUM,empno,ename,job FROM emp WHERE ROWNUM > 5 AND ROWNUM <= 10;
當生成結果集時,Oracle首先會產生一條ROWNUM爲1的記錄,顯然不符合條件,那麼同樣會繼續產生第二條數據,同樣標識ROWNUM爲1,該條記錄同樣繼續被過濾掉,後續生成的ROWNUM依然爲1,因此上述查詢語句不會有任何查詢結果,所以如果想要使上述結果有滿足條件的結果集,必須使用子查詢(把rownum查出來後放在一個虛表中作爲這個虛表的字段再根據條件查詢),代碼如下:

SELECT * FROM (SELECT ROWNUM nums,emp.* FROM emp) WHERE nums > 5 AND nums <= 10;
子查詢部分固定表裏面數據的記錄編號,外層查詢過濾子查詢裏面固定的記錄編號就可以實現Oracle分頁!


二、ROWID僞列

同ROWNUM僞列不同的是,它是物理存在的,ROWID是一種數據類型,它使用基於64bit編碼的18個字符來唯一標識一條記錄物理位置的一個ID,類似於Java中一個對象的哈希碼,都是爲了唯一標識對應對象的物理位置,需要注意的是ROWID雖然可以在表中進行查詢,但是其值並未存儲在表中(既然是僞列,就說明表中並不會物理存儲ROWID的值),所以不支持增刪改操作,下面看個例子:

SELECT ROWNUM,ROWID,empno,ename,job FROM emp WHERE ROWNUM <= 5;
結果如下:


可以看到ROWID確實由18個字符組成,組成結構如下:

數據對象編號 文件編號 塊編號 行編號
OOOOOO FFF BBBBBB RRR
至於ROWID的作用,由於ROWID用來唯一標識表中數據的唯一性,所以可以利用這個特性去除重複,舉個例子,首先運行下述兩行代碼:

  1. CREATE TABLE dept_bak AS SELECT * FROM dept;
  2. INSERT INTO dept_bak SELECT * FROM dept;
得到一個如下的數據庫表


很明顯,數據有重複的,當然你也可以用一個很原始的方法,就是將有重複記錄的表中的數據導到另外一張表中,最後再倒回去。

SQL>create table emp_tmp as select distinct* from emp;
SQL>truncate table emp;        //清空表記錄
SQL>insert into empselect * from emp_tmp;//將臨時表中的數據添加回原表

但是要是emp的表數據是百萬級或是更大的千萬級的,那這樣的方法顯然是不明智的,因此我們可以根據rowid來處理,rowid具有唯一性,查詢時效率是很高的,那麼就可以利用這個特性去重,簡單示例代碼如下:

 DELETE FROM dept_bak 
     WHERE ROWID NOT IN(SELECT MIN(ROWID) 
                            FROM dept_bak GROUP BY DEPTNO); 

再例如,學生表中的姓名會有重複的情況,但是學生的學號是不會重複的,如果我們要刪除學生表中姓名重複只留學號最大的學生的記錄,怎麼辦呢?可以參考如下做法:

DELETE FROM stu a
    WHERE ROWID NOT IN (SELECT MAX(ROWID)
                          FROM stu b
                         WHERE a.name = b.name
                           AND a.stno < b.stno);

除了上述之外還有好多作用,詳情參照Oracle官方文檔!,在此不多做敘述!

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