日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

oracle分页性能不同,oracle高效分页

發(fā)布時(shí)間:2025/4/16 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 oracle分页性能不同,oracle高效分页 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

什么是分頁查詢

對(duì)于基于Web的應(yīng)用而言,對(duì)查詢的結(jié)果集進(jìn)行分頁是一個(gè)比較常見的需求。假設(shè)瀏覽器界面每頁可以顯示10條記錄,最初界面顯示頭10條記錄給用戶,當(dāng)終端用戶點(diǎn)擊“下一頁”按鈕時(shí),界面顯示接下來的10條記錄。一般來說,Web后臺(tái)服務(wù)程序并不是一次性的把所有符合條件的記錄都返回給瀏覽器,再由瀏覽器應(yīng)用程序?qū)Σ樵兘Y(jié)果進(jìn)行分頁。現(xiàn)在的普遍做法都是:當(dāng)用戶要瀏覽下一頁時(shí),瀏覽器重新從WEB后臺(tái)服務(wù)器取出下10條記錄。

對(duì)于采用了數(shù)據(jù)庫的WEB應(yīng)用來說,如何對(duì)查詢的結(jié)果進(jìn)行分頁就有兩種實(shí)現(xiàn)方式,一種是WEB后臺(tái)程序把全部查詢結(jié)果取到內(nèi)存中,由它實(shí)現(xiàn)分頁。另一種是每次只從數(shù)據(jù)庫取出10條記錄,由數(shù)據(jù)庫實(shí)現(xiàn)分頁。

這兩種分頁方式各有優(yōu)缺點(diǎn),有時(shí)可能把這兩種方式結(jié)合起來應(yīng)用。在這里,我主要介紹一下如何在Oracle數(shù)據(jù)庫中實(shí)現(xiàn)分頁查詢。

如何實(shí)現(xiàn)分頁查詢

認(rèn)識(shí)ROWNUM

Oracle的ROWNUM偽列是實(shí)現(xiàn)結(jié)果集分頁的關(guān)鍵,可能有很多人對(duì)于ROWNUM偽列到底代表什么還不太清楚,有人甚至認(rèn)為它是數(shù)據(jù)庫表中記錄的編號(hào)。下面我引用在ASKTOM網(wǎng)站上的他一個(gè)例子幫助大家認(rèn)識(shí)一下ROWNUM到底為何物。為了幫助大家理解,我建了一個(gè)測試表,然后再插入20條測試數(shù)據(jù),當(dāng)前原例子中查詢語句表名和字段也做了相應(yīng)的修改。

--建測試表

createtable t_testrownum

( ridnumber, rvalue varchar2(30))

--插入測試數(shù)據(jù)

begin

insert into t_testrownum values(1, 'aaaa');

insert into t_testrownum values(2, 'aaaa');

insert into t_testrownum values(3, 'aaaa1');

insert into t_testrownum values(4, 'aaaa');

insert into t_testrownum values(5, 'aaaa');

insert into t_testrownum values(6, 'aaaa');

insert into t_testrownum values(7, 'aaaa');

insert into t_testrownum values(8, 'aaaa4');

insert into t_testrownum values(9, 'aaaa');

insert into t_testrownum values(10, 'aaaa');

insert into t_testrownum values(11, 'aaaa');

insert into t_testrownum values(12, 'aaaa');

insert into t_testrownum values(13, 'aaaa5');

insert into t_testrownum values(14, 'aaaa');

insert into t_testrownum values(15, 'aaaa');

insert into t_testrownum values(16, 'aaaa');

insert into t_testrownum values(17, 'aaaa');

insert into t_testrownum values(18, 'aaaa8');

insert into t_testrownum values(19, 'aaaa');

insert into t_testrownum values(20, 'aaaa');

end;

例1.

select * from t_testrownum where rownum = 1;返回結(jié)果集的第一條記錄。那么select * from T where rownum = 2;應(yīng)該返回結(jié)果集的第二條記錄。可是實(shí)際上第二個(gè)查詢語句不會(huì)返回任何記錄,為什么呢?

類似的例子還有:

select * from T where rownum >= 1 and rownum <= 5;返回前5條記錄,而

select * from T where rownum >= 2 and rownum <= 5;無記錄返回。

其實(shí):ROWNUM并是記錄編號(hào),而是Oracle在向外輸出結(jié)果集中的記錄時(shí)給它賦的一個(gè)順序號(hào)。當(dāng)不在查詢語句中限制ROWNUM時(shí),其處理邏輯如下所示:

rownum= 1

forx in ( select * from T )

loop

if ( x satisifies the predicate )

then

OUTPUT the row

rownum = rownum + 1

end if;

endloop;

當(dāng)限制ROWNUM時(shí),我們對(duì)比一下下面兩個(gè)查詢的執(zhí)行計(jì)劃:

語句1:select* from t_testrownum;

語句1的執(zhí)行計(jì)劃:

SELECT STATEMENT Optimizer Mode=CHOOSE TABLE

ACCESS FULL USDPD502.T_TESTROWNUM

語句2:select* from t_testrownum where rownum <= 10;

語句2的執(zhí)行計(jì)劃

SELECT STATEMENT Optimizer Mode=CHOOSE

COUNT STOPKEY

TABLE ACCESS FULL USDPD502.T_TESTROWNUM

通過對(duì)比,我們可以看出語句2的執(zhí)行計(jì)劃中增加了一條‘COUNT STOPKEY’,該句的意思是當(dāng)rownum已超出指定范圍時(shí),停止輸出,其處理邏輯如下:

rownum= 1

forx in ( select * from T )

loop

if( x satisifies the predicate )

then

OUTPUT the row

rownum= rownum + 1

endif;

if ( rownum已超出指定范圍)

then

跳出循環(huán)

endif;

endloop;

至此,我們就可以解釋上面兩個(gè)例子中的問題了。當(dāng)我們限制rownum=1時(shí),第一條記錄滿足該條件,輸出該記錄,rownum增1,由于rownum已超出范圍,停止輸出。當(dāng)我們限制rownum=2時(shí),由于第一條記錄不滿足條件,不輸出該記錄,rownum也不增加。接著取第二條記錄,由于rownum此時(shí)還是1,不滿足條件,同樣也不輸出,如此直到遍歷全部記錄結(jié)束循環(huán)。

基本的分頁查詢

當(dāng)知道rownum是怎么回事后,我們就可以利用它來實(shí)現(xiàn)分頁查詢了。假如我們想從表T中取出第11條到第20條記錄,在未透徹了解ROWNUM之前,許多人可能會(huì)寫出下面的查詢語句。

--語句1

select* t_testrownum a where rownum >= 11 and rownum <= 20;

通過前面的分析,我們知道,這樣的寫法是錯(cuò)誤的。所以,我們把它修改為如下的寫法。

--語句2

select*

from( select a.*, rownum r from t_testrownum a )

wherer>= 11 and r <= 20

該語句的輸出結(jié)果是正確的,它的內(nèi)層查詢先從表t_testrownum中查詢出所有記錄,同時(shí)為每條記錄賦一個(gè)順序編號(hào)r,外層查詢?cè)傧拗浦贿x取編號(hào)為11到20之間的記錄。

從查詢效率上考慮一下,如果我們只需要得到第11到20條之間的記錄,那么在內(nèi)層查詢中就可以利用rownum限制內(nèi)層查詢輸出的記錄數(shù)。修改后的語句如下:

--語句3

select*

from( select a.*, rownum r

fromt_testrownum a where rownum <= 20)

wherer>= 11

需要排序的分頁查詢

有人會(huì)想,排序那還不簡單嗎,加上order by子句就行了。

--語句4

select*

from( select a.*, rownum r from t_testrownum a where rownum <= 20 order by rvalue)

wherer>= 11

我們都知道order by是對(duì)輸出的結(jié)果集進(jìn)行排序,而不是先排序然后輸出結(jié)果集。語句4的實(shí)際效果是,從表t_testrownum中取出前20行記錄,然后按照rvalue字段排序,輸出排序編號(hào)大于等于11的記錄。

只對(duì)前20條記錄進(jìn)行排序顯然不是我們所期望的,為避免這個(gè)問題,有人可能會(huì)把上面的語句做如下修改:

--語句5

select*

from( select a.*, rownum r from t_testrownum a order by rvalue)

wherer>= 11 and r <= 20

同樣,由于rownum在排序之前就確定了,我們得到得記錄并不是排序后的第11到20條記錄,而是排序前的第11到20條記錄。為得到我們期望的結(jié)果,我們必須把rownum r放到order by的外面。修改后的查詢語句如下。

--語句6

select*

from( select b.*, rownum r

from( select a.*

fromt_testrownum a order by rvalue ) b

where rownum <= 20 )

wherer>= 11

如果排序字段rvalue的值在表t_testrownum中是唯一的,那么上面的語句從功能實(shí)現(xiàn)上來說,就沒什么問題了。但是如果rvalue字段的值不唯一,假設(shè)按rvalue排序后,前1到20條記錄的rvalue字段的值是相同的,我們先查出第1到10條記錄,然后再查出第11到20條記錄,這是我們會(huì)發(fā)現(xiàn),同一條記錄可能同時(shí)出現(xiàn)在這兩個(gè)查詢結(jié)果集中。這是為什么呢。一開始,我認(rèn)為是Oracle采用的排序算法是不穩(wěn)定的,兩個(gè)相同的值在兩次排序中的順序是不固定的。但是我們把語句select a.* from t_testrownum a order by rvalue執(zhí)行10次,卻發(fā)現(xiàn)輸出結(jié)果集的排序順序都是一致的。那么是什么導(dǎo)致排序不一致呢。為此,我們觀察了一下語句6的執(zhí)行計(jì)劃:

SELECT STATEMENT, GOAL = CHOOSE

VIEWObject owner=USDPD502

COUNT STOPKEY

VIEW Object owner=USDPD502

SORT ORDER BY STOPKEY

TABLE ACCESS FULL Objectowner=USDPD502 Objectname=T_TESTROWNUM

從執(zhí)行計(jì)劃中我們看出,執(zhí)行計(jì)劃的第2步是“SORT ORDER BY STOPKEY”,它表示

其并不是對(duì)所有符合條件的記錄完全排序,而是僅僅找到符合排序條件的指定條數(shù)的記錄,比如我們限制rownum <= 20,則只需找到排序在前20位的記錄。記得在Oracle的官方文檔上我曾經(jīng)見到Oracle聲稱其排序是穩(wěn)定的一致的。前面我們將select a.* from t_testrownum a order by rvalue執(zhí)行10次,發(fā)現(xiàn)排序是一致的。那么“SORT ORDER BY STOPKEY”方式的排序是否是一致的呢?我們將語句6執(zhí)行10次同樣發(fā)現(xiàn),其結(jié)果是一致的。那么為什么我們用語句6查第1到10條記錄和11到20條記錄時(shí),有些記錄為什么在這兩個(gè)查詢中出現(xiàn)的名次并不一致呢。

--語句7

select*

from( select a.* from t_testrownum a order by rvalue )

where rownum <= 20

--語句8

select*

from( select a.* from t_testrownum a order by rvalue )

where rownum <= 10

為了找出排序不一致的原因,我們分別執(zhí)行語句7和語句8,這時(shí)你會(huì)發(fā)現(xiàn),前10名的記錄在兩次查詢中并不一樣。為此,我們得出結(jié)論,當(dāng)stopkey不同時(shí),排序結(jié)果是不同的。為什么會(huì)這樣呢,大師TOM的解釋是“SORT ORDER BY STOPKEY”是Oracle為優(yōu)化TOPN(查詢排序后的前N條記錄)查詢采用的一種算法。大致的思想是:先取出為排序時(shí)前面的N條記錄,對(duì)這N條記錄排序,然后用后面的剩下的所有記錄依排序要求插入前N條記錄中。一般來說,這樣的插入排序也應(yīng)該是穩(wěn)定的,那為什么N不同,排序結(jié)果就不一樣呢?下面的兩條查詢語句似乎可以給你一點(diǎn)啟發(fā):

--語句9

selecta.*, rownum r

fromt_testrownum a

whererownum <= 10

orderby rvalue

--語句10

select a.*, rownum r

fromt_testrownum a

whererownum <= 20

orderby rvalue

它們的執(zhí)行計(jì)劃如下:

SELECT STATEMENT, GOAL = CHOOSE

SORT ORDER BY

COUNT STOPKEY

TABLE ACCESS FULL Object owner=USDPD502 Object name=T_TESTROWNUM

從上面兩條查詢語句的結(jié)果我們可以看出,排在前面的10記錄也不是一致的。要注意的是,這兩條語句的執(zhí)行計(jì)劃中并沒有使用“SORT ORDER BY STOPKEY”算法。而是普通的排序“SORT ORDER BY”。只是這兩次排序的記錄條數(shù)不一樣,這時(shí)有些人可能會(huì)懷疑是在排序前兩次查詢輸出記錄的順序就是不一樣的。我們可以這么測試一下,先對(duì)t_testrownum表的所有20條記錄排序,然后從表中刪除掉后10條記錄,從語句9和語句10中刪除掉where rownum <= N條件,我們發(fā)現(xiàn),查詢結(jié)果和語句9和語句10是一樣的。由此,我們可以得出結(jié)論,當(dāng)參與排序的記錄數(shù)量不同時(shí),具有相同值的記錄的排序順序是不同的。

進(jìn)行分頁查詢時(shí),如果同一條記錄在多個(gè)分頁中出現(xiàn),這樣的結(jié)果肯定不是你所期望的。為了避免這種現(xiàn)象的發(fā)生,一個(gè)簡單的方法就是在排序條件中增加輔助排序字段,使得每條記錄的組合排序字段是唯一的。

如何在分頁查詢中避免排序

對(duì)于需要排序的分頁查詢來說,如果參與排序的結(jié)果集很大,而實(shí)際返回的記錄數(shù)很少,那么有兩點(diǎn)是需要注意的:第一大結(jié)果集排序?qū)ο到y(tǒng)資源的占用,第二如果排序字段的值不唯一,某些記錄會(huì)出現(xiàn)在多個(gè)分頁中。如何避免以上的兩點(diǎn)呢,我們知道,索引的鍵值是有序組織的,我們是否可以利用索引來避免排序呢。答案是肯定的,我們?cè)趓value上建立索引:

--語句11

createindex idx_testrownum_rvalue on t_testrownum(rvalue);

這時(shí),我們把語句6修改一下:

--語句12

select*

from( select b.*, rownum r

from( select a.*

fromt_testrownum a

wherervalue > chr(1) order by rvalue ) b

where rownum <= 20 )

wherer>= 11

執(zhí)行語句12,它的執(zhí)行計(jì)劃如下:

SELECT STATEMENT, GOAL = CHOOSE

VIEW Object owner=USDPD502

COUNT STOPKEY

VIEW Object owner=USDPD502

TABLE ACCESS BY INDEX ROWID Object owner=USDPD502 Object name=T_TESTROWNUM

INDEX RANGE SCAN Object owner=USDPD502 Object name=IDX_TESTROWNUM_RVALUE

從上面的執(zhí)行計(jì)劃中,我們已看不到“SORT ORDER BY STOPKEY”字樣,說明沒有排序步驟。那么記錄在分頁中重復(fù)的問題是否也解決了呢,經(jīng)過測試,該問題也不復(fù)存在。

那么如果我們需要降序排序呢?對(duì)于降序排序,我們需要增加相應(yīng)的hints來提示優(yōu)化器走降序索引掃描。

-語句13

select *

from( select b.*, rownum r

from( select/*+index_desc(a idx_testrownum_rvalue)*/a.*

fromt_testrownum a

wherervalue > chr(1) order by rvalue desc ) b

where rownum <= 20 )

wherer>= 11

語句13的執(zhí)行計(jì)劃如下:

SELECT STATEMENT, GOAL = CHOOSE

VIEW Object owner=USDPD502

COUNT STOPKEY

VIEW Object owner=USDPD502

TABLE ACCESS BY INDEX ROWID Object owner=USDPD502 Object name=T_TESTROWNUM

INDEXRANGESCAN DESCENDING Objectowner=USDPD502

利用索引避免排序需要的注意點(diǎn)

雖然使用索引來避免排序是一個(gè)好方法,但是,任何事物都不可能是十全十美的,使用該方法時(shí)需要注意以下幾點(diǎn):

1)排序字段上的索引必須是升序索引,如果使用降序索引將導(dǎo)致升序排序時(shí)分頁出現(xiàn)問題。(具體是什么原因我現(xiàn)在還沒弄明白,如果有知道原因的可以指點(diǎn)一下)

2)在分頁開始記錄數(shù)大于10000后,利用索引排序進(jìn)行分頁的性能反而不如直接排序分頁的方式好。(如果字段是數(shù)字性,性能下降不大,如果是字符串則性能下降明顯,這可能和字符串和數(shù)字的比較方式不同有關(guān))

總結(jié)

以上是生活随笔為你收集整理的oracle分页性能不同,oracle高效分页的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。