最近,項目中的一個 DB2分頁查詢很慢 ,組長將此分頁的優化分派給了我;而後一頓優化(亂操做)後,將DB2分頁查詢耗時降到了比較滿意的狀況,[ 開森 ];前端
而後立刻將結果報告了組長,組長查看個人演示後,發現分頁查詢確實快了不少,能夠達到讓人「接受的程度」,比優化以前的 頁面一直轉圈等待 至關能夠了呀;sql
注:優化後的演示環境與發現分頁查詢慢時的環境基本一致,包括庫中數據量、DB2的配置、服務器的配置等。服務器
首先經過查看執行計劃發現,SQL語句中的索引都利用上了,那麼暫時就不是 索引 的問題了,最後發現是 SQL語句存在問題 ,對SQL進行了優化,查詢就快了;微信
下面就簡單描述下DB2的分頁SQL是怎麼進行優化的,binggou 走起;網絡
在對查詢SQL語句進行優化時,須要知道其邏輯執行順序,這對進行SQL優化有很大幫助的;學習
SQL的 邏輯執行順序 ,指的是SQL語句按照必定的規則,一整條語句應該如何執行,每個關鍵字、子句部分在什麼時刻執行;fetch
上面簡單描述了下查詢SQL的邏輯執行順序,下面就來分析下查詢慢的分頁語句的邏輯執行順序;優化
SELECT * FROM ( SELECT B.*, ROWNUMBER() OVER() AS RN FROM ( select ts.name, ts.age, tc.class_name, ts.describe, ts.birthday from t_student ts LEFT JOIN t_class tc on tc.class_id = ts.class_id where ts.age = 23 AND ts.birthday >= TO_DATE('2020-06-12','yyyy-MM-dd') AND ts.birthday <= TO_DATE('2020-07-15' , 'yyyy-MM-dd') order by ts.birthday desc ) AS B )AS A WHERE A.RN BETWEEN 10 AND 20
t_student 表中存在二級索引:index(age, birthday) spa
t_class 表的主鍵:class_idcode
經過上面SQL的邏輯執行順序分析得知有三處地方能夠嘗試進行下優化,看看查詢是否是變快了;
由於後面的where條件篩選中,都是對主表 t_student 表進行的篩選,因此能夠提早使用where條件對t_student表進行篩選,獲得虛擬表,
而後使用這個虛擬表和鏈接的表 t_class 計算笛卡爾積,此時笛卡爾積已經小不少了;
SQL語句以下:
SELECT * FROM ( SELECT B.*, ROWNUMBER() OVER() AS RN FROM ( select ts.name, ts.age, tc.class_name, ts.describe, ts.birthday from (select class_id, name, age, describe, birthday from t_student where age = 23 AND birthday >= TO_DATE('2020-06-12','yyyy-MM-dd') AND birthday <= TO_DATE('2020-07-15' , 'yyyy-MM-dd') ) ts LEFT JOIN t_class tc on tc.class_id = ts.class_id order by ts.birthday desc ) AS B )AS A WHERE A.RN BETWEEN 10 AND 20
在select投影列中,將不須要的大字段 describe 不要放入其中,由於若是放到投影列中的話,查詢時會形成其佔用較多的緩衝池空間,若是致使緩衝池空間滿了的話,就要進行磁盤IO了,磁盤IO很是耗時的;
還有就是若是響應中帶有大字段的內容的話,在進行網絡IO時,會形成傳輸速度變慢,頁面加載數據時也會變慢的;
SQL語句以下:
SELECT * FROM ( SELECT B.*, ROWNUMBER() OVER() AS RN FROM ( select ts.name, ts.age, tc.class_name, ts.birthday from (select class_id, name, age, birthday from t_student where age = 23 AND birthday >= TO_DATE('2020-06-12','yyyy-MM-dd') AND birthday <= TO_DATE('2020-07-15' , 'yyyy-MM-dd') ) ts LEFT JOIN t_class tc on tc.class_id = ts.class_id order by ts.birthday desc ) AS B )AS A WHERE A.RN BETWEEN 10 AND 20
分頁查詢都是根據 pageNo(頁號),pageSize(一頁的數量)進行的所需頁面數據篩選;
例如,當前頁面是第二頁的話,每頁展現數據10條,當前頁數據的篩選,就是 rownum between ((pageNo-1) pageSize) and (pageNo pageSize) ,那麼在分頁前的數據篩選時,可使用 fetch first (page * rows) rows only 限制篩選的數據量,別所有篩選出來了,而是最多隻篩選到當前頁面中最大的行號前便可;
因爲使用了 fetch first (page * rows) rows only ,減小了不少的篩選操做,速度會快不少,特別是點擊前幾頁時,速度都是很是快的,可能越日後翻頁,響應會相應慢些,可是分頁時,基本都是查看前幾頁的,後面的幾乎不多看,因此此時效果看起來是十分好的
SQL語句以下:
select ts.name, ts.age, tc.class_name, ts.birthday from ( select aa.class_id, aa.name, aa.age, aa.birthday from( select class_id, name, age, birthday, ROWNUMBER() OVER () AS RN from t_student where age = 23 AND birthday >= TO_DATE('2020-06-12','yyyy-MM-dd') AND birthday <= TO_DATE('2020-07-15' , 'yyyy-MM-dd') order by birthday desc fetch first 20 rows only ) aa where aa.RN BETWEEN 10 AND 20 ) ts LEFT JOIN t_class tc on tc.class_id = ts.class_id
至此DB2分頁SQL已經基本完成了優化,查詢速度提高了一大半了,可是此分頁SQL還有優化的餘地,能夠經過修改SQL語句和索引 index(age, birthday) 進一步提高查詢速度;
下面在知識擴展部分簡單描述下怎麼再次進行優化;
先來看下面這段SQL語句:
select class_id, name, age, birthday, ROWNUMBER() OVER () AS RN from t_student where age = 23 AND birthday >= TO_DATE('2020-06-12','yyyy-MM-dd') AND birthday <= TO_DATE('2020-07-15' , 'yyyy-MM-dd') order by birthday desc fetch first 20 rows only
這段SQL語句執行時會使用到 二級索引 index(age, birthday) ,因爲在此索引上沒法查詢到所有的字段值,因此須要回表查詢 class_id, name 字段的值,因爲多了回表操做,會致使查詢變慢,因此此時要想辦法避免回表;
只修改索引,將 二級索引 index(age, birthday) 改成 index(age, birthday,name,class_id) 便可,SQL語句不用變更;
可是索引字段變多後,對新增、更新、刪除SQL的操做影響比較大,會致使其執行耗時變長,由於須要對索引進行維護;因此若是這兩張表寫操做比較多的話, 不建議直接修改索引 ;
此方法其實也是須要回表的,可是此時回表次數會比以前回表的次數少的多得多,幾乎能夠忽略不計;
此方法不須要修改索引,而是對SQL語句進行修改, 首先進行 二級索引 index(age, birthday) 查詢時再也不須要返回 class_id, name, age, birthday 字段了,而是隻返回 t_student 表的 主鍵 stu_id ,此時就不須要回表了;
而後會使用到二級索引 index(age, birthday)中birthday默認排序,最後根據 fetch first 20 rows only 只返回前20條數據,此時返回了20條 主鍵 stu_id 值,而後能夠根據主鍵stu_id左外鏈接t_student,得到其它的字段值;
此時至關於只須要回表20次而已,不須要所有進行回表了;
SQL語句以下:
select stu2.class_id, stu2.name, stu2.age, stu2.birthday, ROWNUMBER() OVER () AS RN from ( select stu_id from t_student where age = 23 AND birthday >= TO_DATE('2020-06-12','yyyy-MM-dd') AND birthday <= TO_DATE('2020-07-15' , 'yyyy-MM-dd') order by birthday desc fetch first 20 rows only ) stu1 left join t_student stu2 on stu1.stu_id = stu2.stu_id
最終優化後的分頁SQL以下:
select ts.name, ts.age, tc.class_name, ts.birthday from ( select aa.class_id, aa.name, aa.age, aa.birthday from( select stu2.class_id, stu2.name, stu2.age, stu2.birthday, ROWNUMBER() OVER () AS RN from ( select stu_id from t_student where age = 23 AND birthday >= TO_DATE('2020-06-12','yyyy-MM-dd') AND birthday <= TO_DATE('2020-07-15' , 'yyyy-MM-dd') order by birthday desc fetch first 20 rows only ) stu1 left join t_student stu2 on stu1.stu_id = stu2.stu_id ) aa where aa.RN BETWEEN 10 AND 20 ) ts LEFT JOIN t_class tc on tc.class_id = ts.class_id
在進行分頁查詢時,通常是先查詢下符合條件的總數據量,這個數據量用於分頁,獲得分頁數的;
那麼咱們可能會遇到一些很是特別狀況,查詢總數據量的SQL以下:
select count(*) from t_student ts LEFT JOIN t_class tc on tc.class_id = ts.class_id where ts.age = 23 AND ts.birthday >= TO_DATE('2020-06-12','yyyy-MM-dd') AND ts.birthday <= TO_DATE('2020-07-15' , 'yyyy-MM-dd')
上面這個SQL語句其實能夠進行優化的,它其實不用關聯 t_class 表的, 具體緣由以下:
直接查詢主表中知足條件的數據便可,所以能夠將多餘的錶鏈接去掉,這會大大提高SQL的查詢速度;
八卦下,分析分析爲何有人會這麼寫呢?
猜測主要是爲了圖方便,直接複製的分頁SQL語句進行改的 [啼笑皆非] ;
若是本文對您有幫助的話,請揮動下您愛發財的小手點下贊呀,您的支持就是我不斷創做的動力,謝謝啦!
您能夠微信搜索 【木子雷】 公衆號,大量Java學習乾貨文章,您能夠來瞧一瞧喲!