Oracle僞列rownum的學習【轉載】

Hibernate的分頁,裏邊講到了Oracle下Hibernate是使用rownum來實現分頁的。

Oracle 9i Complete Reference中關於rownum的敘述是:

ROWNUM returns the sequence number in which a row was returned when first① selected② from a table. The first row has a ROWNUM of 1③, the second is 2, and so on. Note, however, that even a simple order by in the select statement my disorder the ROWNUMs, which are assigned to the rows before any ordering takes place.
對於這段描述,有3處須要特別注意的地方(定義中①②③標註的部分)。
詳細解釋以下:
1.ROWNUM是在記錄第一次從表中被select出來的時候賦的值。裏邊的first就是這個第一次的意思。

2.ROWNUM是記錄從表中被select出來時賦的值。而不是選出來後,對被選出來的記錄進行某些操做後才賦值。這裏常常出現的一個問題是如何用order by 和rownum結合解決TOP-N;

3.ROWNUM是對應一次select活動的,而不是對應一個表的。 在包含子查詢的查詢中,每次查詢活動均可以產生一個ROWNUM,這些相同記錄的ROWNUM是不一樣的;

4.ROWNUM是在記錄被select出來的時候產生的。 第1條記錄的ROWNUM值爲1,第2條記錄爲2….若是查詢條件包含了ROWNUM而且與ROWNUM取值規律矛盾,則不會有任何記錄被選出。

TOP-n
ROWNUM是在select出記錄的時候生成的,發生在order by 以前。若是試圖經過order by 和rownum來解決TOP-N問題,就須要特別當心這一點。

例如
SELECT Empno, Ename, Job, Mgr, Hiredate, Sal
      FROM Emp
      ORDER BY Sal DESC WHERE ROWNUM <11
是不會選出Sal最小的10條記錄的。

ROWNUM 是在記錄被select出來時產生的,它先與任何排序的。上述語句的實際執行步驟與SQLServer中的TOP字句是不一樣的。 爲了select出工資最高的5個員工,你必須經過子查詢強制在使用ROWNUM條件以前進行排序。這就是解決top-N問題的經常使用辦法。

SELECT Empno, Ename, Job, Mgr, Hiredate, Sal
   FROM
   (SELECT Empno, Ename, Job, Mgr, Hiredate, Sal
      FROM Emp
      ORDER BY NVL(Sal, 0) DESC)
   WHERE ROWNUM < 6;

另外,在網上找到帖子提到了例外狀況:若是order by的字段上有主鍵或索引,則oracle先排序,而後再rownum。我沒有作驗證,但我猜測,這應該體現了oracle的內部實現機制:若是order by字段上沒有索引,則先選出知足條件的全部記錄,而後再排序。若是有索引,則選出來的時候,記錄就是字段上的索引按順序出來的。因此,ROWNUM是在排序的基礎上分配的。

不過,在javaeye,找到一篇文章(連接爲:
http://forum.javaeye.com/viewtopic.php?=9681&postdays=0&postorder=asc&start=0)。

做者說即便用子查詢,當知足必定條件的時候,這個分頁機制也是不正確的。這篇帖子是2004年的,後來討論的人就很少了,robbin等大蝦也沒有來討論。是否像他所說,我還得驗證驗證。

指定記錄ROWNUM的上下限

ROWNUM是一個僞字段,是在記錄被select出來時產生的,並非實際存在的字段。它的取值從1開始的。

ROWNUM直接出如今where字句中的合理用法有3種:
rownum = 1; rownum < n; rownum <= n。除此以外的任何形式都是得不到任何記錄的。例如 rownum=2; rownum > 5。
下邊詳細說明緣由。rownum = 2 得不到記錄,由於查詢返回的第一條記錄其ROWNUM必須爲1。可是rownum=2又不容許這樣的記錄被返回。所以,它永遠不會返回第一條記錄,也永遠沒法找到知足rownum=2的記錄。 對於rownum>5。This is because the pseudo column rownum never reaches 6. Rownum counts actually returned rows. In order for where rownum > 5 to be true, 5 rows must already have returned, but they are not, because these were excluded through exactly this where clause. 我的以爲在WHERE裏使用ROWNUM讓人感到有點亂:ROWNUM既做爲條件同時又做爲select出來記錄時產生的數據(相似select出來的字段)。而ROWNUM又不是一個真正存在的字段,只是在select出來記錄的時候產生的。彷佛有一點先有雞仍是先有蛋的意思。 這裏,能夠破壞這個亂的根源:把ROWNUM這個僞字段變成一個真正的字段。方法是子查詢。在子查詢中把ROWNUM select出來,而後定義一個別名。此時,ROWNUM就做爲查詢結果集的一個真正字段存在了。以後,在外層查詢中用這個別名來作任何條件均可以了。以下述語句所示: select name , price from (    Select rownum r, name, price from items) where r>5 This works because Oracle first evaluates the inner select statement and returns all records with an increasing rownum. The outer where clause can then select the rows it needs. 不過,這種方法有一個問題,就是若是表的數據量很大,會致使查詢結果耗用大量的系統資源。 子查詢的ROWNUM select tt.*, rownum  num2 from (select rownum num_, t.* from t_moi t ) tt where tt.num_ >2 and tt.num_<6 這裏邊的num2和num_的值是不相同的,也就是兩次select有各自的rownum。 總結 看來,用子查詢能夠解決ROWNUM的不少問題。
相關文章
相關標籤/搜索