據孔老先生說,茴香豆的茴字有四種寫法,那oracle的分頁查詢又有多少種寫法呢?
sql
分頁查詢,其實本質上就是topN查詢的變種, 若是把topN的一部分結果集去掉,就變成了分頁.性能優化
topN的基本寫法,兩層select,第一層先order by,第二層再用rownum:微信
select owner,object_name,object_id,rownum as rn from(select * from t1 where owner='SYS' order by object_id desc) where rownum<=20;oracle
有些初級開發人員有時會寫成:函數
select owner,object_name,object_id,rownum as rn from t1 where owner='SYS' and rownum<=20 order by object_id desc;性能
這種寫法的邏輯可能存在問題,由於sql解析時會先執行rownum,隨機先選出20條記錄,再執行排序. 而不是常見業務須要的先排序,再取前20. 若是知足條件的所有結果集<=20, 那麼邏輯也是沒問題的.fetch
在上面topn的基礎上, 再套一層select, 就變成了最多見的標準的三層select的分頁查詢寫法(第一層排序,第二層給rownum取別名,獲得topn,第三層去掉topn的前面部分):
優化
select owner,object_name,object_id,rn fromspa
(select a.*,rownum as rn from.net
(select * from t1 where owner='SYS' order by object_id desc) a where rownum<=20
) where rn>10;
執行計劃中看到COUNT STOPKEY 爲最佳(沒有sort字樣).
除了上面比較常見的寫法, 還有其餘幾個不常見的寫法:
1層select(12c+才支持的offset 寫法,有時可能須要使用hint來糾正優化器執行計劃):
select owner,object_name,object_id,rownum as rn
from t1
where owner='SYS'
order by object_id desc
offset 10 rows fetch next 10 rows only;
執行計劃中看到WINDOW NOSORT STOPKEY爲最佳
2層select,用到了row_number分析函數(可能須要使用hint來糾正優化器執行計劃):
SELECT * FROM
(SELECT owner,object_name,object_id,
row_number() over (order by object_id desc) as rn
FROM t1
where owner='SYS'
) WHERE RN<= 20 and RN > 10;
執行計劃中看到WINDOW NOSORT STOPKEY爲最佳
4層select,對於頁數比較大的分頁查詢,某些狀況下可使用:
with tmp as
(SELECT * FROM
( SELECT rid, ROWNUM as RN
FROM
(SELECT rowid as rid
FROM t1
where owner='SYS'
order by object_id desc
) WHERE ROWNUM <= 500
) WHERE RN > 490
) select /*+ use_nl(a) leading(b) */ owner,object_name,object_id,rn
from t1 a,tmp b
where a.rowid=b.rid;
下面的3層寫法,是比較常見的低效分頁寫法,在分頁前結果集大的狀況,性能會比較差, 須要避免使用:
select * from
(
select a.*,rownum as rn
from
(select owner,object_name,object_id
from t1
where owner= 'SYS'
order by object_id desc
)a
) where rn>10 and rn<=20;
執行計劃通常包含 SORT ORDER BY 的步驟.
掌握了分頁寫法,只是優化的第一步,下面咱們看一個生產案例,SQL代碼以下:
這是一個取topn的SQL,先取topn(分頁前結果集20萬左右),再left join,寫法徹底沒問題,可是執行時間仍是比較長,須要24秒:
用hint調下執行計劃,執行時間變成1秒:
hint: /*+ monitor leading(p o) push_pred(co@sel$2) */
若是再建立一個core_userprofile表上orgid+UpdateDate+id 3字段聯合索引, 那麼這個SQL的執行時間估計也就是10毫秒如下了. (從24秒到10毫秒,這種性能的提升,靠硬件是沒法實現的,現實中確實有不少相似的SQL,惋惜的是,咱們不少的決策人員, 只相信高級硬件才能解決性能問題,不知道有這些高級優化技巧)
總結:
分頁查詢,寫法只是第一步,寫法正確的基礎上,若是執行計劃不佳,咱們能夠經過oracle優化器提供的hint來調整執行計劃(不須要改sql代碼); 可是若是sql寫法不佳,也是沒有辦法經過調整索引和執行計劃進行優化.
寫法和索引,是SQL優化的核心,在此基礎上經過hint調整執行計劃, 是更高級的技術, 須要更進一步的瞭解優化器特性,以人腦優化器代替電腦優化器.
想提升SQL優化技能,看完個人線上培訓課程(索引專題,SQL寫法與改寫專題)會大有幫助.
本文分享自微信公衆號 - 老虎劉談oracle性能優化(sql_tigerliu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。