Oracle 實現分頁查詢
當資料量太大,例如咱們資料庫的一個表裡,符合查詢條件的有幾十萬條資料,如果你一次性查詢出來的話,不僅查詢速度很慢,響應時間太長影響使用者體驗,而且使用者也看不了這麼多資料。
可能有人會說可以在後臺進行分頁類封裝處理(邏輯分頁
),那你首先要把查詢到的幾十萬條資料存在記憶體裡,這樣看來,還是推薦資料庫分頁查詢(物理分頁
)吧
一、Oracle採用ROWNUM
實現分頁
兩種格式的查詢效率對比(格式1更快速)
首先,兩種格式都有三層查詢。
CBO優化模式下,Oracle可以將外層的查詢條件,推到內層(只能向內推一層)查詢中,以提高內層查詢的執行效率。
對於格式1的第二層查詢條件,WHERE ROWNUM <=end (page*pagesize)
-- 格式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,在第一層的時候就將資料過濾好了,再將量減少了的資料向外返回,是不是效率自然就高了起來。
注意:上述兩種格式,針對最內層是複雜多表聯合的查詢,也同樣適用。
UNION
、UNION ALL
、MINUS
、INTERSECT
、GROUP BY
、DISTINCT
、UNIQUE
以及聚集函式如MAX
、MIN
和分析函式
等操作是針對,全部符合條件的資料的結果集,的一個操作,所以如果最內層的子查詢中包含了這些操作中的一個以上,那麼ROWNUM
會不起作用,分頁查詢的效能等於沒實現。
Oracle10g的新功能GROUP BY STOPKEY
,使得Oracle10g解決了GROUP BY操作分頁效率低的問題。在10g以前,Oracle的GROUP BY操作必須完全執行完,才能將結果返回給使用者。但是Oracle10g增加了GROUP BY STOPKEY
執行路徑,使得使用者在執行GROUP BY
操作時,可以根據STOPKEY
隨時中止正在執行的操作。這使得標準分頁函式對於GROUP BY
操作重新發揮了作用。
二、ROWNUM
操作執行原理
- 執行查詢操作;
- 將第一行的ROWNUM置為1;
- 將得到的行的ROWNUM與條件相比較,如果不匹配,則拋棄行,如果匹配,則返回行;
- oracle獲取下一行,然後將ROWNUM增1;
- 返回第3步;
- 直到超過ROWNUM的限制條件;
ROWNUM 使用注意事項
- ROWNUM不能以任何基表的名稱作為字首。
- 子查詢中的ROWNUM必須要有別名,否則還是不會查出記錄來,這是因為ROWNUM不是某個表的列,如果不起別名的話,無法知道ROWNUM是子查詢的列還是主查詢的列。
- 查詢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的前面了(排序演算法的穩定性評判標準)。
解決辦法:
- 排序的時候,除了需要欄位,跟一個有索引的欄位,例如主鍵;
- 跟ROWID也可以,這種方法最簡單,且對效能的影響最小。