1. 程式人生 > >Oracle 實現分頁查詢

Oracle 實現分頁查詢


當資料量太大,例如咱們資料庫的一個表裡,符合查詢條件的有幾十萬條資料,如果你一次性查詢出來的話,不僅查詢速度很慢,響應時間太長影響使用者體驗,而且使用者也看不了這麼多資料。

可能有人會說可以在後臺進行分頁類封裝處理(邏輯分頁),那你首先要把查詢到的幾十萬條資料存在記憶體裡,這樣看來,還是推薦資料庫分頁查詢(物理分頁)吧

一、Oracle採用ROWNUM實現分頁

兩種格式的查詢效率對比(格式1更快速)

首先,兩種格式都有三層查詢。
CBO優化模式下,Oracle可以將外層的查詢條件,推到內層(只能向內推一層)查詢中,以提高內層查詢的執行效率。
對於格式1的第二層查詢條件,WHERE ROWNUM <=end (page*pagesize)

就可以被推入內層查詢中,這樣的話,Oracle查詢的結果一旦超過了ROWNUM的限制條件,就終止查詢,將結果返回了。

-- 格式1(推薦)
SELECT * FROM   
(  
SELECT temp.*, ROWNUM RN   
FROM (SELECT * FROM 表名) temp  
WHERE ROWNUM <=end (page*pagesize)  
)  
WHERE RN >=start (page-1)*pagesize

但是格式2的ROWNUM限制條件,是放在了第三層的查詢語句中,Oracle無法把第三層的內推到第一層(即使推到最內層也沒有意義,因為最內層不知道RN是啥,RN是定義在第二層的)

-- 格式2
SELECT * FROM   
(  
SELECT temp.*, ROWNUM RN   
FROM (SELECT * FROM TABLE_NAME) temp   
)  
WHERE RN BETWEEN start (page-1)*pagesize AND end (page*pagesize)

因此,對於格式2查詢語句,最內層返回給第二層所有符合條件的資料,第二層返回給第三層所有符合條件的資料,過濾資料發生在第三層;而格式1,在第一層的時候就將資料過濾好了,再將量減少了的資料向外返回,是不是效率自然就高了起來。

注意:上述兩種格式,針對最內層是複雜多表聯合的查詢,也同樣適用。

UNIONUNION ALLMINUSINTERSECTGROUP BYDISTINCTUNIQUE以及聚集函式如MAXMIN分析函式等操作是針對,全部符合條件的資料的結果集,的一個操作,所以如果最內層的子查詢中包含了這些操作中的一個以上,那麼ROWNUM會不起作用,分頁查詢的效能等於沒實現。

Oracle10g的新功能GROUP BY STOPKEY,使得Oracle10g解決了GROUP BY操作分頁效率低的問題。在10g以前,Oracle的GROUP BY操作必須完全執行完,才能將結果返回給使用者。但是Oracle10g增加了GROUP BY STOPKEY執行路徑,使得使用者在執行GROUP BY操作時,可以根據STOPKEY隨時中止正在執行的操作。這使得標準分頁函式對於GROUP BY操作重新發揮了作用。

二、ROWNUM

操作執行原理

  1. 執行查詢操作;
  2. 將第一行的ROWNUM置為1;
  3. 將得到的行的ROWNUM與條件相比較,如果不匹配,則拋棄行,如果匹配,則返回行;
  4. oracle獲取下一行,然後將ROWNUM增1;
  5. 返回第3步;
  6. 直到超過ROWNUM的限制條件;

ROWNUM 使用注意事項

  1. ROWNUM不能以任何基表的名稱作為字首。
  2. 子查詢中的ROWNUM必須要有別名,否則還是不會查出記錄來,這是因為ROWNUM不是某個表的列,如果不起別名的話,無法知道ROWNUM是子查詢的列還是主查詢的列。
  3. 查詢ROWNUM在某區間的資料,ROWNUM對小於某值的查詢條件為true,對於大於某值的查詢條件直接認為是false的,但是可以間接的讓它轉為認為是true的。那就必須使用子查詢。

三、FIRST_ROWS

對於表連線來說,在寫分頁查詢的時候,可以考慮增加FIRST_ROWS提示,它會導致CBO選擇NESTED LOOP,有助於更快的將查詢結果返回。
其實,不光是表連線,對於所有的分頁查詢都可以加上FIRST_ROWS提示。

-- 格式3
SELECT FIRST_ROWS * FROM   
(  
SELECT temp.*, ROWNUM RN   
FROM (SELECT * FROM TABLE_NAME) temp   
)  
WHERE RN BETWEEN start (page-1)*pagesize AND end (page*pagesize)

不過需要注意的時,分頁查詢的目標是儘快的返回前N條記錄,因此,無論是ROWNUM還是FIRST_ROWS機制都是提高前幾頁的查詢速度,對於分頁查詢的最後幾頁,採用HASH JOIN的方式,執行效率幾乎沒有任何改變,而採用NESTED LOOP方式,則效率嚴重下降,而且遠遠低於HASH JOIN的方式。

四、排序的列不唯一

當用來排序的列存在值相等的行,可能會造成資料重複出現,因為Oracle的排序演算法不具有穩定性,即鍵值相等的資料A和B,在排序之前A在B的前面,在排序之後A不一定在B的前面了(排序演算法的穩定性評判標準)。

解決辦法:

  1. 排序的時候,除了需要欄位,跟一個有索引的欄位,例如主鍵;
  2. 跟ROWID也可以,這種方法最簡單,且對效能的影響最小。

參考連結1
參考連結2