最近做的項目需要實現一個分頁查詢功能,自己先看了別人寫的方法:
1 <!-- 查詢 --> 2 <select id="queryMonitorFolder" parameterType="monitorFolderQuery" resultMap="monitorFolderMap"> 3 select 4 id, name, type, var_num, erp, createTime, modifyTime 5 from 6 monitor_folder 7 where 8 yn = 1 9 <if test="name != null"> 10 and name like concat('%',#{name,jdbcType=VARCHAR},'%') 11 </if> 12 and <![CDATA[id <=]]> 13 ( select 14 id 15 from 16 monitor_folder 17 where 18 yn=1 19 <if test="name != null"> 20 and name like concat('%',#{name,jdbcType=VARCHAR},'%') 21 </if> 22 order by id desc limit #{startRowNum},1) 23 order by id desc 24 limit #{pageSize} 25 </select>
當時我個人的思路是:在分頁中使用了兩次查詢,效率比較低。直接使用limit就好了。
查過資料之后,不管是自己的想法還是這個代碼都是有不足的。
limit的標准寫法:
1 SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
比如要查第十個開始的十個,就是limit 10, 10
許多網站的分頁功能的底層實現就是依賴前端給后端傳來一些參數,比如起始頁和頁面大小,來結合limit控制分頁。但是這個地方首先有一個問題:效率。
如果這么寫:
1 SELECT * FROM student LIMIT 10000 , 10;
實際底層執行是從第一行開始找到10010行,再拋棄前面的一萬行。所以當用戶往后翻到很多頁的時候,offset這個值可能就比較大,實際執行效率就會很慢。
一般有幾種做法來彌補。
1,用方法計算出開始和結尾條件,用where語句對查詢進行限制。
1 SELECT * FROM student WHERE ID >= 9990 AND ID <= 9999;
比如前端傳來:pageIndex = 1000, pageSize = 10。 也就是第一千頁,每頁十條。得到這兩個參數之后,直接用方法計算得到需要查詢的數據條件:WHERE id >= 9999 AND id <= 9999。
這樣做的顯著好處就是速度快並且也很好理解。而顯著壞處就是使用限制。什么意思呢,就是說你計算出的這個id的范圍不一定能全部命中。一般數據庫的刪除是修改某個字段表示刪除,這樣的話你計算的id范圍可能就有的記錄被刪除了,同時,前端可能還傳來別的限制條件,比如發帖日期在一個月之內,誰發的帖等等。所以問題的核心就是不能保證計算的范圍都能匹配上。如果這里做的比較粗糙(比如后端這么做,前端也直接拿這些數據顯示),實際的效果就是用戶點開一頁,明明顯示說10條記錄,結果只有5條,而且每一頁的情況還不一致。
2,where語句限制一半,limit控制行數
1 SELECT * FROM student WHERE ID >= 9990 LIMIT 10;
這樣的好處就是不僅效率高了,而且能剛好拿十條。但是這里依然還是有一個問題:不適用於所有情況。具體不適用與什么情況呢?簡單來說就是id和行數不能對應的情況。比如ID本身是無規律離散的,那么計算這個起始ID就不能簡單的pageIndex * pageSize了。
