2. DB2分页优化技术
在WEB查询中,我们一般将大量数据分切成页,一页一页地推送给BROWER端。另外,基本表信息中又大量使用字典表,如果将这些数据与字典表关联,将会极大加重服务器的重担。
如有一个记录均超过100W以上记录的A表,里面有一个A1,A2,A3,A4这四个字段,表示某个字典的代码,为了关联出与它们对应的代码意义(设它们的字典分别为D_A1,D_A2,D_A3,D_A4),你可以这样写相关的SELECT-
SQL,
Select a.*,a1.a1_NAME,a2.a2_NAME,a3.a3_NAME,a4.a4_NAME From A Left join D_a1 a1 on A.a1=A1.A1 Left join D_a2 a2 on A.a2=A2.A2 Left join D_a3 a3 on A.a3=A3.A3 Left join D_a4 a4 on A.a4=A4.A3
此
SQL当然没有什么错误,而且也能关联出结果。但问题是:在100W以上的记录上去关联字典表(如果字典表也比较大的话),这会是一个很耗时的查询。下面给出一个优化
SQL(每页25条记录,显示满足条件的第二页):
select t.*, a.字典名1, a.字典名2 from ( SELECT t.* FROM (select a.*, rownumber() over() AS MyTempRecordeNo from [你要查询的表] a Where [你的条件] ) t WHERE t.MyTempRecordeNo BETWEEN 26 AND 50 ) t left join [字典表1] a ON 相关条件1 left join [字典表2] b ON 相关条件2 with ur
使用存储过程的通用分页方式:
db2 通用分页存储过程CREATE PROCEDURE DB2ADMIN.PROC_GETPAGE
(INOUT CURRENTPAGE INTEGER ,
IN STRSQL VARCHAR ( 500 ),
IN PAGESIZE INTEGER ,
OUT TOTALPAGE INTEGER ,
OUT TOTALRECORD INTEGER
)
SPECIFIC DB2ADMIN.SQL080201103307110
DYNAMIC RESULT SETS 1
LANGUAGE SQL
NOT DETERMINISTIC
CALLED ON NULL INPUT
READS SQL DATA
INHERIT SPECIAL REGISTERS
BEGIN
DECLARE tmpsql VARCHAR ( 1000 ); -- 主语句
DECLARE orderFiled VARCHAR ( 50 );
DECLARE s VARCHAR ( 1000 );
DECLARE v_Start INTEGER ;
DECLARE v_End INTEGER ;
DECLARE result CURSOR WITH RETURN TO CALLER FOR S2;
![]()
SET orderFiled = getOrderField(strsql);
![]()
if (length(orderFiled ) > 0 ) then
set orderFiled = ' order by ' || orderFiled ;
end if ;
![]()
SET s = getSQL(strsql);
set tmpsql = ' select count(*) from ( ' || strsql || ' ) as a ' ;
prepare s2 from tmpsql;
open result;
fetch result into totalrecord; -- 总记录数
close result;
![]()
if (pagesize = 0 ) then
set pagesize = 20 ; -- 每页显示数
end if ;
set totalPage = (totalrecord - 1 ) / pagesize + 1 ; -- 总页数
![]()
if (currentPage < 1 ) then
set currentPage = 1 ; -- 当前页
else
if (currentPage > totalPage) then
set currentPage = totalPage;
end if ;
end if ;
set v_Start = (currentPage - 1 ) * pagesize ;
set v_End = currentPage * pagesize;
set tmpsql = ' select * from ' ||
' (select rownumber() over() as row, ' ||
' w.* from ( select * from ( ' || s || ' ) n ' || orderFiled || ' ) w) w1 where row between ' || char (v_Start ) || ' and ' || char (v_End );
![]()
prepare s2 from tmpsql ;
open result;
![]()
END ;
使用了两个java函数
import java.util.regex.*;import COM.ibm.db2.app.UDF;public class FunctionDB2 extends UDF
{
public static String getOrderField(String sql)
{
Pattern Regex = Pattern.compile("select(.+)order\\s*by\\s*(.+)",
Pattern.CANON_EQ | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
Matcher RegexMatcher = Regex.matcher(sql);
if (RegexMatcher.find()) {
return RegexMatcher.group(2);
}
return "";
}
public static String getSQL(String sql)
{
Pattern Regex = Pattern.compile("(.+)order\\s*by\\s*(.+)",
Pattern.CANON_EQ | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
Matcher RegexMatcher = Regex.matcher(sql);
if (RegexMatcher.find()) {
return RegexMatcher.group(1);
}
return sql;
}
}
另外的通用分页存储过程:CREATE PROCEDURE SALES.DB2PAGINATION(IN ITBNAME VARCHAR(2000),-- 表名
IN ISHOWFIELD VARCHAR(1000),-- 显示字段
IN IJOIN VARCHAR(1000),-- 联接条件(如:内联、外联)
IN IWHERE VARCHAR(2000),-- 查询条件 (注意: 不要加 WHERE)
IN IORDER VARCHAR(100),-- 排序条件 (注意: 不要加 ORDER BY)
IN IPAGESIZE INTEGER,-- 页尺寸 如果为0 默认返回前一百万条数据可以认为是返回所有数据
INOUT IOCURRENTPAGEIX INTEGER,-- 输入和输出:当前页
OUT OPAGESTARTROW INTEGER,-- 输出:当前开始行
OUT OPAGEENDROW INTEGER,-- 输出:当前结束行
OUT OTOTALROWS INTEGER,-- 输出:当前总记录数
OUT OHASPREVIOUSPAGE INTEGER,-- 输出:是否有上一页
OUT OHASNEXTPAGE INTEGER,-- 输出:是否有下一页
OUT OTOTALPAGES INTEGER,-- 输出:总页数
OUT OERROR VARCHAR(100))-- 输出:错误信息
RESULT SETS 1
MODIFIES SQL DATA
NOT DETERMINISTIC
LANGUAGE SQL
BEGIN
/**//*----------------------------------------------------------------
* Copyright (C) 2007 Huacius
* 版权所有。
*
* 存储过程分页
*
*
//-----------------------------------------------------------------------*/
DECLARE STRSQL VARCHAR(6000);-- 主语句DECLARE result CURSOR WITH RETURN TO CALLER FOR S2;DECLARE exit handler FOR sqlexception-- 异常捕获
BEGIN
set OERROR = 'error!';
END;-- BODY start --
if(iwhere <> '') then
set iwhere = ' where ' || iwhere;
end if;
if(iorder <> '') then
set iorder = 'order by ' || iorder;
end if;
if(ijoin <> '') then
set ijoin = ' ' || ijoin;
end if;
set strsql = 'select count(*) from ' || itbname || ijoin || iwhere;
prepare s2 from strsql;
open result;
fetch result into ototalrows;-- 总记录数
close result;if(ipagesize = 0) then
set ipagesize = 1000000;-- 每页显示数
end if;set ototalpages = (ototalrows - 1) / ipagesize + 1;-- 总页数if(iocurrentpageix < 1) then
set iocurrentpageix = 1;-- 当前页
else
if(iocurrentpageix > ototalpages) then
set iocurrentpageix = ototalpages;
end if;
end if;set opagestartrow = ipagesize * (iocurrentpageix -1) + 1;-- 每页开始数
if(iocurrentpageix = ototalpages) then
set opageendrow = ototalrows;-- 每页结束数
else
set opageendrow = ipagesize * iocurrentpageix;
end if;if(iocurrentpageix > 1) then
set ohaspreviouspage = 1;-- 是否有上一页
else
set ohaspreviouspage = 0;
end if;if(iocurrentpageix < ototalpages) then
set ohasnextpage = 1;-- 是否有下一页
else
set ohasnextpage = 0;
end if;set strsql = 'select * from (select rownumber() over(' || iorder || ') as rownum,'
|| ishowfield
|| ' from '
|| itbname
|| ijoin
|| iwhere
|| ') as temp where rownum between ' || rtrim(char(opagestartrow)) || ' and '|| rtrim(char(opageendrow));
prepare s2 from strsql;
open result;
-- BODY end --END
DB2存储过程分页测试CREATE PROCEDURE TEST ( IN IN_START SMALLINT,
IN IN_SIZE SMALLINT )
DYNAMIC RESULT SETS 1
------------------------------------------------------------------------
-- DB2下通过传递参数控制显示记录的范围-- SQL 存储过程
-- IN_START 记录起点
-- IN_SIZE 记录条数
------------------------------------------------------------------------
P1: BEGIN
-- 声明游标
DECLARE cursor1 CURSOR WITH RETURN FOR
SELECT *
FROM (SELECT ROW_NUMBER() OVER() as ROW_NO, T.* FROM TOPICIS.DM_HY AS T) AS TEMP
WHERE TEMP.ROW_NO > IN_START
AND TEMP.ROW_NO < IN_START + IN_SIZE;OPEN cursor1;
END P1DB2分页查询
SELECT * FROM (Select 字段1,字段2,字段3,rownumber() over(ORDER BY 排序用的列名 ASC) AS rn from 表名) AS a1 WHERE a1.rn BETWEEN 10 AND 20以上表示提取第10到20的纪录select * from (select rownumber() over(order by id asc ) as rowid from table where rowid <=endIndex ) AS a1
where a1.rowid > startIndexDB2 SQL查询结果集自动编号、返回条数范围控制研究
摘要:DB2 SQL语句实现查询的记录自动编号、并根据编号进一步实现记录范围控制、分页查询。
环境:IBM DB2 Universal V9.1Windows XP Professional要求:写一个SQL语句,在查询结果中要显示出记录序号,这个序号是是根据记录顺序自动生成的。测试初始化DB2 SQL脚本:drop table t_test;create table t_test(bs bigint not null not null generated by default as identity,username varchar(20) not null,password varchar(20),remark varchar(200));comment on t_test(bs is '标识',username is '用户名',password is '密码',remark is '备注');insert into t_test(username, password) values ('aaa', 'aaa'),('bbb', 'bbb'),('ccc', 'ccc'),('ddd', 'ddd'),('eee', 'eee'),('fff', 'fff'),('ggg', 'ggg');实现:1、实现查询记录编号要求:查询bs不等于1的所有记录。select t.*, ROW_NUMBER() OVER() as ROW_NOfrom t_test twhere t.bs <> 1查询结果如下:bs username password remark ROW_NO---------------------------------------2 'bbb' 'bbb' '[Null]' 13 'ccc' 'ccc' '[Null]' 24 'ddd' 'ddd' '[Null]' 35 'eee' 'eee' '[Null]' 46 'fff' 'fff' '[Null]' 57 'ggg' 'ggg' '[Null]' 6说明:ROW_NUMBER() OVER() 是用来产生编号序列,从1开始编号,为整数。as ROW_NO 是为编号列设定显示名称。2、实现按照记录范围进行查询要求:要求查询第2-5条记录。select *from (select t.*, ROW_NUMBER() OVER() as ROW_NOfrom t_test t) as wwhere ROW_NO >= 2and ROW_NO <=5查询结果如下:bs username password remark ROW_NO---------------------------------------2 'bbb' 'bbb' '[Null]' 23 'ccc' 'ccc' '[Null]' 34 'ddd' 'ddd' '[Null]' 45 'eee' 'eee' '[Null]' 53、实现SQL分页查询对2中的SQL进行扩展,将范围起始编号作为变量传递给SQL就可以实现分页查询了。当然这个还需要程序的支持,掌握这个原理就很容易了,在此不做介绍。总结:对记录返回控制是数据库系统的特性,不同的数据库系统有不同的实现。SQL直接对结果集记录控制效率上一般是最好的。结合高级编程语言,可以实现复杂的分页查询。DB2 SQL TOP 解决方式:我们都知道在sql server中的如下sql语句是正确的
select top 20 * from tablename order by id desc
而在DB2中,这句话就存在问题了,系统会报错。解决方法如下:
select * from tablename order by id desc fetch first N rows only
这样就可以了我的语句是strSQL = "select CostBill_Master.*,GT_USERINFO.USRNAM from CostBill_Master,GT_USERINFO where CostBill_Master.DPTIDT='"+branchId+"' and GT_USERINFO.USRIDT=CostBill_Master.USERIDT order by BillNo desc fetch first 10 rows only";