oracle中row_number和rownum的區別和聯系(翻譯)


http://www.tuicool.com/articles/bI3IBv

附問題:有以下一個SQL語句:

SELECT * FROM ( SELECT t.*, row_number() OVER (ORDER BY ID) rn FROM mytable t ) WHERE rn BETWEEN :start and :end

sql中的order by語句大大降低了處理的速度,如果把order by去掉,相應的執行計划會大大地提高。如果換成下面的sql:

SELECT t.*, row_number() OVER (ORDER BY ID) rn FROM mytable t WHERE rownum BETWEEN :start and :end

很明顯,這個sql是錯的,根本查詢不了正確的數據信息。是否有其它的方法可以提高查詢速度? 
針對以上問題,就必須要了解一下關於row_number和rownum的區別,以及如何來運用這些信息。

首先了解一下rownum是如何進行工作的,根據oracle的官方文檔: 
如果對rownum進行大於比較,這個比較將直接返回false。如,下列sql語句將不能返回任何數據信息:

SELECT * FROM employees WHERE ROWNUM > 1

在查詢中,第一條被命中的數據將賦予一個偽列rownum為1,那么這個條件就為false。第二條被命中的數據由於第一條的false將重新成為第一條數據,那么仍然賦值為1,顯示這個條件仍然為false。后續所有的數據將重復執行這個邏輯,最后一條數據也沒有返回。

這就是為什么之前的第2個查詢,應該轉換為以下的sql語句:

SELECT * FROM ( SELECT t.*, ROWNUM AS rn FROM mytable t ORDER BY paginator, id ) WHERE rn BETWEEN :start and :end

接下來,需要通過創建一些臨時數據表來查看這個sql語句的執行性能,我們將創建臨時表,追加索引,然后填充數據,最后分析這個sql語句的查詢信息。

CREATE TABLE mytable ( id NUMBER(10) NOT NULL, paginator NUMBER(10) NOT NULL, value VARCHAR2(50) ) / ALTER TABLE mytable ADD CONSTRAINT pk_mytable_id PRIMARY KEY (id) / CREATE INDEX ix_mytable_paginator_id ON mytable(paginator, id) / INSERT INTO mytable(id, paginator, value) SELECT level, level / 10000, 'Value ' || level FROM dual CONNECT BY level <= 1000000 / COMMIT / BEGIN DBMS_STATS.gather_schema_stats('"20090506_rownum"'); END; /

這個Sql語句創建一個包括100萬條數據的表,並且創建一個聯合索引. 
同時,在這個查詢中,patinator字段是不是惟一的,是為了在之后展示這樣一種現象:
在查詢中,某些數據可能在不同的分頁查詢中出現多次,而某些數據則可能根據不會被查詢出 
這就是所謂的分頁混亂。

然后,分別使用row_numer和rownum分別進行查詢,返回從900001到900010之間的10條數據信息。 
row_number()

SELECT * FROM ( SELECT t.*, ROW_NUMBER() OVER (ORDER BY paginator, id) AS rn FROM mytable t ) WHERE rn BETWEEN 900001 AND 900010
ID PAGINATOR VALUE RN
900001 90 Value 900001 900001
900002 90 Value 900002 900002
900003 90 Value 900003 900003
900004 90 Value 900004 900004
900005 90 Value 900005 900005
900006 90 Value 900006 900006
900007 90 Value 900007 900007
900008 90 Value 900008 900008
900009 90 Value 900009 900009
900010 90 Value 900010 900010
10 rows fetched in 0.0005s (0.8594s)
SELECT STATEMENT VIEW WINDOW NOSORT STOPKEY TABLE ACCESS BY INDEX ROWID, 20090506_rownum.MYTABLE INDEX FULL SCAN, 20090506_rownum.IX_MYTABLE_PAGINATOR_ID

rownum

SELECT * FROM ( SELECT t.*, ROWNUM AS rn FROM ( SELECT * FROM mytable ORDER BY paginator, id ) t ) WHERE rn BETWEEN 900001 AND 900010
ID PAGINATOR VALUE RN
900001 90 Value 900001 900001
900002 90 Value 900002 900002
900003 90 Value 900003 900003
900004 90 Value 900004 900004
900005 90 Value 900005 900005
900006 90 Value 900006 900006
900007 90 Value 900007 900007
900008 90 Value 900008 900008
900009 90 Value 900009 900009
900010 90 Value 900010 900010
10 rows fetched in 0.0005s (0.7058)
SELECT STATEMENT VIEW COUNT VIEW TABLE ACCESS BY INDEX ROWID, 20090506_rownum.MYTABLE INDEX FULL SCAN, 20090506_rownum.IX_MYTABLE_PAGINATOR_ID

從上文中,可以看出,使用rownum的查詢速度略快於row_number函數。 
然后再看一個row_number查詢,可以看出oracle足夠的智能,它可以通過使用聯合索引而避免進行排序操作,然后通過使用stopkey操作,可以直接快速查找到相應的數據信息。 
rownum查詢也同樣使用索引,但並沒有利用stopkey條件,只是簡單的計數操作。 
那么,能否同樣讓rownum使用stopkey呢。在之前的查詢中,oracle並不知道這個rn就是在內層查詢rownum的別名,我們可以重寫查詢,在外層查詢中使用rownum,這樣就可以在外層利用stopkey條件了。這就是我們常見的oracle3層分頁的變形:

SELECT * FROM ( SELECT t.*, ROWNUM AS rn FROM ( SELECT * FROM mytable ORDER BY paginator, id ) t ) WHERE rn >= 900001 AND rownum <= 10
ID PAGINATOR VALUE RN
900001 90 Value 900001 900001
900002 90 Value 900002 900002
900003 90 Value 900003 900003
900004 90 Value 900004 900004
900005 90 Value 900005 900005
900006 90 Value 900006 900006
900007 90 Value 900007 900007
900008 90 Value 900008 900008
900009 90 Value 900009 900009
900010 90 Value 900010 900010
10 rows fetched in 0.0005s (0.4714s)
SELECT STATEMENT COUNT STOPKEY VIEW COUNT VIEW TABLE ACCESS BY INDEX ROWID, 20090506_rownum.MYTABLE INDEX FULL SCAN, 20090506_rownum.IX_MYTABLE_PAGINATOR_ID

在這個查詢中,oracle利用了stopkey,同時速度只有471ms,比原來更快。

如果row_number和rownum使用同樣的執行計划,但為什么rownum明顯更快呢。 
這是因為:oracle的歷史實在是太久了,而不同的時間導致相同的特性卻有不同的效果。

rownum在oracle6中被引進,發布時間為1988年,在當時什么資源和條件都不滿足的情況下,作為一個簡單的計數器,被認為是非常簡單和高效的。 
而隨着時代的發展,更多的需求被提及出來,這時,一個相當於但功能比rownum更強大的函數被引入,這就是row_number函數,它從oracle9i開始被引進。這時,效率已經不再是惟一的條件了,所以row_number的實現也不再以效率為惟一的指標了。

當然,如果你有更多的要求,如分組排序等,則需要使用row_number函數,但如果你僅僅是簡單的分頁查詢,建議使用rownum,這也是為什么在現在的時代rownum還是這么流行(據說在oracle12c中有offset分頁操作符了,內部同樣使用row_number函數,這樣rownum可以退休了)

以下是英文原文:http://explainextended.com/2009/05/06/oracle-row_number-vs-rownum/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM