七郎's JavaBlog

          草木竹石皆可為劒。至人之用人若鏡,不將不迎,應(yīng)而不藏,故能勝物而不傷。
          posts - 60, comments - 14, trackbacks - 0, articles - 0
           

          ORACLE數(shù)據(jù)庫分頁查詢/翻頁

          最佳實(shí)踐

           

           

          一、示例數(shù)據(jù):

          Select Count(*) From dba_objects ;

          -------------------------------------------------

              COUNT(*)

                 6254

          Create table ao as select * f rom dba_objects ;

          Create index i_ao_created on ao(created) ;

          Create index i_ao_id on ao(object_id)  ;

          Create table bo as select * from dba_objects where rownum<=100 ;

          Analyze table ao compute statistics for table for all indexes ;

          Analyze table bo compute statistics ;

           

          二、求頁數(shù)

          無論以本文中哪種SQL寫法,為了獲得查詢的總頁數(shù),不可避免的總要進(jìn)行一次COUNT(*) 的查詢過程,SQL如下:

          單表:

          Select count(*) into rowcount from ao where created > … and object_id between …

          多表:

          Select count(*) into rowcount from ao a,bo b where ao.object_id = bo.object_id and ao.object_name like …. And bo.ojbect_id > …

           

          設(shè)單頁行數(shù)為 pagesize (缺省為 20) ,則總頁數(shù) pagecount =  ceil(rowcount / pagesize)

          CEIL函數(shù): 返回大于或等于給出數(shù)字的最小整數(shù)
          SQL> select ceil(21.000001),ceil(1),ceil(10.1234) from dual;

           

          CEIL(21.000001)    CEIL(1)     CEIL(10.1234)

          ---------------        ----------      -------------

                  22              1            11

           

          提示:在具體項(xiàng)目中做分頁實(shí)現(xiàn)時(shí),設(shè)計(jì)上如果不要求算出總頁數(shù),則可以省去此COUNT(*) 的查詢,則整個(gè)翻頁性能將有所提升(因?yàn)樯僮隽艘粋€(gè)查詢)

           

          綜上所述,對(duì)于每次的分頁查詢,需要輸入的參數(shù)有2個(gè):

          頁號(hào):   pageindex 

          頁行數(shù): PAGESIZE (一般為整個(gè)系統(tǒng)設(shè)一個(gè)常量,如前面定義的20

           

          則每個(gè)頁面應(yīng)該顯示的記錄為:

          IF pageindex <= pagecount THEN

          Firstrow = (pageindex – 1)*PAGESIZE + 1

          Lastrow = pageindex * PAGESIZE

          END IF;

           

          三、分頁查詢實(shí)現(xiàn)SQL方案

           

          方案一:(單表分頁查詢)

          適用場(chǎng)景:

          A、 WHERE部分條件僅僅通過索引查詢就可滿足所有查詢過濾條件;

          B、滿足A條件,且有ORDER BY ,且WHERE 條件中所使用的索引,正好適合 ORDER BY 字段的要求。

          SQL如下:

          select /*+ ordered use_nl(v3 t) */
           t.*
           from (select v2.rd
                    from (select v1.rd, rownum rn
                            from (select a.rowid rd
                                    from ao a
                                   where
          a.created >= sysdate - 100
                                  
          order by created) v1
                           where rownum <= 40) v2   -- 40
          lastrow
                   where rn >= 21) v3,               -- 21
          firstrow
                 ao t
           where v3.rd = t.rowid

           

          執(zhí)行計(jì)劃:

          0      SELECT STATEMENT Optimizer=CHOOSE

           1    0   NESTED LOOPS

           2    1     VIEW

           3    2       COUNT (STOPKEY)

           4    3         VIEW

           5    4           INDEX (RANGE SCAN) OF 'I_AO_CREATED' (NON-UNIQUE)

           6    1     TABLE ACCESS (BY USER ROWID) OF 'AO'

           

          評(píng)價(jià):該種場(chǎng)景下的分頁SQL寫法執(zhí)行效率最高效,但限制較多(見適用場(chǎng)景)。

           

          不適用場(chǎng)景:

          以上述SQL為例,如果WHERE 條件部分為 a.created >= sysdate – 100 and a.object_name like ‘ABC%’ and … 即不適應(yīng)于場(chǎng)景A條件所述,或有ORDER BY ,如:ORDER BY OBJECT_NAME ,CREATED ,即不適應(yīng)于場(chǎng)景B條件所述,則方案一SQL不適用。

           

          方案二:(單表/多表分頁查詢)

          適用場(chǎng)景:

          所有場(chǎng)景都適用,包括方案一所指場(chǎng)景。

          SQL如下(單表):


           select v2.*
             from (select v1.*, rownum rn
                     from (select object_name,
                                  object_id,
                                  owner,
                                  data_object_id,
                                  created
                             from ao
                            where created >= sysdate - 100
                              and owner = 'SYS'
                            order by object_name, created
          ) v1
                    where rownum <= 40) v2      -- 40
          lastrow
           where rn >= 21                       -- 21
          firstrow

           

          執(zhí)行計(jì)劃:

          0      SELECT STATEMENT Optimizer=CHOOSE

          1    0   VIEW

          2    1     COUNT (STOPKEY)

          3    2       VIEW

          4    3         SORT (ORDER BY STOPKEY)

          5    4           TABLE ACCESS (BY INDEX ROWID) OF 'AO'

          6    5             INDEX (RANGE SCAN) OF 'I_AO_CREATED' (NON-UNIQUE)

           

          SQL如下(多表)

           select v2.*
           from (select v1.*, rownum rn
                    from (select ao.object_name,
                                 ao.owner,
                                 ao.created,
                                 bo.object_type,
                                 bo.last_ddl_time
                            from ao, bo
                           where ao.object_id = bo.object_id
                             and ao.created >= sysdate - 300
                             and ao.owner = 'SYS'
                             and bo.status = 'VALID'
                           order by ao.object_name, bo.created
          ) v1
                   where rownum <= 40) v2      -- 40
          lastrow
           where rn >= 21                       -- 21: firstrow

           

          執(zhí)行計(jì)劃:

          0      SELECT STATEMENT Optimizer=CHOOSE

          1    0   VIEW

          2    1     COUNT (STOPKEY)

          3    2       VIEW

          4    3         SORT (ORDER BY STOPKEY)

          5    4           TABLE ACCESS (BY INDEX ROWID) OF 'AO'

          6    5             NESTED LOOPS

          7    6               TABLE ACCESS (FULL) OF 'BO'

          8    6               INDEX (RANGE SCAN) OF 'I_AO_ID'

           

          評(píng)價(jià):以上兩種(單表/多表)的分頁查詢寫法很類似,從性能上來說都遜于方案一的做法,但這和場(chǎng)景有關(guān),是沒有辦法的事。

           

           

          綜述:本文所述的兩種分頁方案,中心是闡述分頁的實(shí)現(xiàn)思想,即翻頁由數(shù)據(jù)庫查詢來完成,不占用額外的前后端會(huì)話內(nèi)存,不在前端/中間層緩存數(shù)據(jù),實(shí)現(xiàn)翻頁時(shí)方法盡量簡(jiǎn)單、實(shí)用,需要強(qiáng)調(diào)的是,不論以哪種方案實(shí)現(xiàn)翻頁,都要求查詢的主體部分(SQL中粗體部分)的WHERE條件要求高效,這樣整個(gè)翻頁的執(zhí)行速度才會(huì)快。方案一是取合理的利用了索引的特性;方案二則在主查詢不變的基礎(chǔ)上通過靈活運(yùn)用ROWNUM偽列,實(shí)現(xiàn)查詢行的定位,具體應(yīng)用當(dāng)中能夠使用哪種方案,需具體評(píng)估場(chǎng)景,在之前請(qǐng)深刻理解本文兩種方案中示例SQL的實(shí)現(xiàn)。

          應(yīng)當(dāng)特別注意一個(gè)問題,在獲得分頁的查詢SQL時(shí),注意WHERE條件部分不應(yīng)該是拼出來的,而應(yīng)該是以綁定變量的方式設(shè)置條件字段值?。?/span>

          SQL形如:

          select v2.*
             from (select v1.*, rownum rn
                     from (select object_name,
                                  object_id,
                                  owner,
                                  data_object_id,
                                  created
                             from ao
                            where created >=
          :created
                              and owner =
          :owner
                            order by object_name, created) v1
                    where rownum <=
          :lastrow) v2     
           where rn >=
          :firstrow

           


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 普格县| 南昌市| 青浦区| 大丰市| 安多县| 顺平县| 龙游县| 玉田县| 建湖县| 伊通| 华坪县| 始兴县| 仙游县| 来安县| 离岛区| 渭源县| 沐川县| 介休市| 平果县| 西贡区| 沁水县| 衡阳市| 石河子市| 井研县| 延长县| 漾濞| 肃宁县| 云和县| 余江县| 自治县| 皮山县| 宁海县| 泸溪县| 红原县| 宜阳县| 三明市| 石柱| 拜泉县| 曲周县| 太白县| 邓州市|