DB2 sql優化

 
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
使用存儲過程的通用分頁方式:
 
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 )>0then
         
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 = 0then
        
set pagesize = 20;-- 每頁顯示數
    end if

    
set totalPage = (totalrecord - 1/ pagesize + 1;-- 總頁數 



    
if(currentPage < 1then
        
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 P1
 
 
DB2分頁查詢
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 > startIndex
 
 
 
DB2 SQL查詢結果集自動編號、返回條數範圍控制研究

摘要:
DB2 SQL語句實現查詢的記錄自動編號、並根據編號進一步實現記錄範圍控制、分頁查詢。
 
環境:
IBM DB2 Universal V9.1
Windows 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_NO
 from t_test t
where t.bs <> 1
 
查詢結果如下:
bs  username password remark    ROW_NO 
---------------------------------------
2   'bbb'   'bbb'   '[Null]'    1
3   'ccc'   'ccc'   '[Null]'    2
4   'ddd'   'ddd'   '[Null]'    3
5   'eee'   'eee'   '[Null]'    4
6   'fff'   'fff'   '[Null]'    5
7   'ggg'   'ggg'   '[Null]'    6
 
說明:ROW_NUMBER() OVER() 是用來產生編號序列,從1開始編號,爲整數。
as ROW_NO 是爲編號列設定顯示名稱。
 
2、實現按照記錄範圍進行查詢
要求:要求查詢第2-5條記錄。
select *
  from (select t.*, ROW_NUMBER() OVER() as ROW_NO
          from t_test t) as w
 where ROW_NO >= 2
 and ROW_NO <=5
 
查詢結果如下:
bs  username password remark    ROW_NO 
---------------------------------------
2   'bbb'   'bbb'   '[Null]'    2
3   'ccc'   'ccc'   '[Null]'    3
4   'ddd'   'ddd'   '[Null]'    4
5   'eee'   'eee'   '[Null]'    5
 
3、實現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";
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章