oracle

 

排序分頁,排名次在咱們系統中應用的非常多,當表的數據量大的時候,排序分頁,排名次就成爲了SQL性能的瓶頸,生產庫對這類SQL多有預警!

 

最近對海量數據排序分頁,排名次做了深入研究,終於找到一個很好的方法,表的數據量對這種方法的性能影響不大,只與記錄所在的頁數有關,頁數越大,性能稍有降低,第一個分頁最快。

 

對數據量爲10000000的表在開發庫上做了測試,每頁顯示20條記錄,第1000頁用了0.187秒,用我們平常用的方法執行了125.238秒 ,比較了下結果和順序都是一樣的。

 

 

新方法sql如下:

 

SELECT/*+ leading(t1) use_nl(t1 t2) */

t1.rn, --名次

t2.*

FROM  kfj_posters_info t2,

       (SELECT/*+ index(t IDX_KFJ_POSTERS_INFO_NUM_DATE)*/

         rownum rn,

         ROWID  rd

        FROM  kfj_posters_info t

        WHERE t.posters_url ISNOTNULL

        AND   rownum <= 20000

        ORDER  BYpseters_vote_num DESC,

                 date_created     DESC) t1

WHERE  t2.rowid= t1.rd

AND   t1.rn > 19980

ORDER  BY t1.rn ASC;

 

說下這個sql的思路:

 

1,  通過pseters_vote_num,date_created兩個列的降序索引IDX_KFJ_POSTERS_INFO_NUM_DATE取出當前頁所有記錄的 rowid (因爲索引中已排好序,這一步非常快);

2,  通過rowid 取出當前頁表的數據(通過rowid 訪問表是最快的方法) ;

3,  爲了保證結果正確,最後再對當前頁的數據做一次排序。

4,  爲了保證執行計劃的正確,這個sql的兩個hint是必須要加的。如果執行計劃不正確可能導致結果不對或很慢。

 

 

新方法的優缺點:

 

優點:性能很好,只對當前頁的記錄排序,消耗的cpu,I/O,內存資源很少

 

缺點:依賴索引,對執行要求很高,如果執行計劃不正確,可能影響結果的正確性,所以必須使用hint去限制sql走正確的執行計劃。

 

 

 

常規方法如下

SELECT *

FROM   (SELECTrownum rn,

              t1.*

        FROM   (SELECT *

               FROM   kfj_posters_info t

               WHERE  t.posters_url ISNOTNULL

               ORDER  BY pseters_vote_num DESC,

                         date_created     DESC) t1

        WHERE  rownum <= 20000

        ) t2

WHERE  t2.rn> 19980

         

 

常規方法的優缺點:

 

優點:執行計劃的正確與否不影響結果

 

缺點:性能很差。對太多沒有必要的數據做order by ,每查一個分頁都要對全表做order by,消耗太多cpu,I/O,內存資源 。

 

 

 

 

=》 如果大家需要對大數據量做排序分頁,排名次操作,請嘗試新方法,謝謝!

 

另外介紹兩個可以排名次的函數

 

dense_rank() over() -- 數值相同的算做一個名次,下個數值的名次直接加 1

 

rank() over()  --數值相同的算做一個名次,下個數值的名次不是加1

 

具體區別大家通過下面sql在開發環境運行下就可以明白了

 

 

SELECT/*+ leading(t1) use_nl(t1 t2) */

t1.rn,

t1.dense_rank_,   -- dense_rank() over(ORDER BY t.pseters_vote_num DESC)

t1.rank_,  -- rank() over(ORDER BY t.pseters_vote_num DESC)

 t2.pseters_vote_num,

t2.date_created,

t2.*

FROM  kfj_posters_info t2,

       (

        SELECT/*+ index(t IDX_KFJ_POSTERS_INFO_NUM_DATE)*/

         dense_rank() over(ORDERBY t.pseters_vote_num DESC) dense_rank_ ,

          rank() over(ORDERBYt.pseters_vote_num DESC)  rank_,

          rownum rn,

          ROWID rd

        FROM  kfj_posters_info t

        WHERE  t.posters_urlISNOTNULL

        AND   rownum <= 20

        ORDER  BYpseters_vote_num DESC

        ) t1

WHERE  t2.rowid= t1.rd

AND   t1.rn > 0

ORDER  BY rn ASC;

 

結果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章