最近,项目中的一个 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学习干货文章,您能够来瞧一瞧哟!