前言
最近在做一個DB2的項目,遇到分頁處理的設計時開始犯難。以前一直采用MySQL作為項目數據庫,其中的Limit關鍵字非常人性化,MySQL把分頁的處理邏輯封裝到了數據庫的核心中,使得做查詢設計時,根本不用過多的考慮分頁的問題。
可是DB2卻把這個難題推到了我們面前。其實不止DB2如此,很多大型的數據庫例如MS SQL Server也不支持分頁關鍵字。當然,DB2中提供了RowNumber函數,同Oracle有一些類似。有很多解決方案都是由此關鍵字得來的。
為了解決這個問題,互聯網上提供了如下幾個解決方案:
方案一:利用JDBC2的數據集。JDBC2數據集中提供了absolute方法,用來在查詢的結果集中進行定位,數據集保存在內存中,你必須告訴JDBC你想定位的絕對位置,這個位置由你來計算。對於海量的數據集,這種方式效率並不高。
方案二:利用DB2自帶的函數 ROWNUMBER() OVER(ORDER BY sort-key),這里的RowNumber函數是通過排序計算出來的行的順序號。根據這個原理,可以先SELECT滿足WHERE條件的所有記錄,然后用ORDER BY排序,在行號的基礎上,結合子查詢的組合得出查詢結果。例如:
select * from ( select rownumber() over(order by foo.bar, foo.baz) as rownumber_, * from foos foo order by foo.bar, foo.baz ) as temp_ where rownumber_ between ?+1 and ?
第三種方案的實現原理其實很簡單。先用一個子查詢從數據庫中選出(Page-1)*PageSize個記錄來,然后用max/min關鍵字(更加排序的方式)取出子查詢中的最大或最小值。外查詢再取出所有滿足大於/小於這個值的前PageSize個記錄。就這么簡潔。
早一些的實現使用了NOT IN/NOT EXIST等關鍵字,后來被MAX/MIN方案所取締,主要是考慮到效率問題。實現者表現出了對於數據庫的精湛技術的同時,也為我們提供了一個優雅的解決方案。
方案比較:
三種方案各有所長。
第一種方案可以算得上是一種獨立的解決方案,不需要任何特殊的數據庫技術支持,只要使用JDBC2.0就可以。但在處理大規模的數據查詢時,需要耗費大量的數據庫游標移動所帶來的性能損耗。
第二種方案是DB2和Oracle采用的解決方案,相比前一種,性能上要好很多。但同第一種相類似,仍然要把所有滿足條件的記錄放到內存中,通過內部的游標查詢來實現分頁。在特定於數據庫的方案中,這種方案是一種可取的方案。另外,在對分組查詢(Group BY)進行分組時,如果采用第三種方案,每一次分頁都將會進行兩次分組查詢,非常的低效,這個時候,采用第一、二中方案就比較理想。
第三種方案是我比較欣賞的風格:優雅。之所以這么說,是因為該方案封裝在存儲過程中,利用了存儲過程編譯后運行的高效性;同時方案本身采用了優雅的通用數據庫技術,通用性和效率都能得到保證。
具體采用什么樣的方案,一般應該根據自己的項目實際情況來定。如果數據量比較小,建議采用第一種方案;如果數據量中等,采用第二種;反之,第三種是在處理大容量數據分頁查詢下的理想方案。
示例
在mysql中,實現分頁查詢比較方便,只需要傳入查詢開始的條數start和要查詢多少條數據即每頁顯示多少條數據limit就可以
db2和Oracle實現分頁的方式類似,所需要的參數為要查詢的數據開始的條數start,結尾的條數end,並且在sql語句中還需要設定row_number()(Oracle中為rownum)作為查詢的輔助函數,此時要使用select語句的嵌套,嵌套在from子句中進行。
子句為:
select row_number() over ( order by date desc ) as r,e.* from emp e
其中
row_number() 作為人為的添加一列作為給每一條數據進行編號
over()中是實現排序的字段和方式,date是字段名,desc是方式,都可以修改,但是over()為必須寫的,不寫會報錯
as r是為row_number()這個列取的一個別名
真正要分頁查詢的數據在外面的父句中進行實現
完整的語句為
select * from (
select row_number() over(ORDER BY date DESC) as r,e.*
from emp e
where e.name=’A’)
where r between 1 AND 5
此時的start為1,end為5,要注意between…and是>=1並且<=5實際上查出的是6條數據,所以要注意limit和end之間的處理,可以在action中進行,否則可能會出現每頁顯示條數不正確或者每一頁的最后一條數據和下一頁第一條數據重復等問題
還有一種方式是用>=和<=直接實現的
語句為
select * from (
select row_number() over(ORDER BY date DESC) as r,e.*
from emp e
where e.name=’A’ AND r<=5)
where r>0
此時顯示的是1-5條數據,因為數據庫是從1開始的。
1. db2分頁查詢sql
select * from (
select ROW_NUMBER() OVER(ORDER BY DOC_UUID DESC) AS ROWNUM, DOC_UUID, DOC_DISPATCHORG, DOC_SIGNER, DOC_TITLE from DT_DOCUMENT ) a
where ROWNUM > 20 and ROWNUM <=30
2.ROW_NUMBER()函數
增加行號,不排序
select * from ( select ROW_NUMBER() OVER() AS ROWNUM,t.* from DT_DOCUMENT t ) a
增加行號,按某列排序
select * from ( select ROW_NUMBER() OVER( ORDER BY DOC_UUID DESC ) AS ROWNUM,t.* from DT_DOCUMENT t ) a