论坛首页 综合技术论坛

讨论一个比较复杂的查询语优化问题

浏览 8067 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-04-16  
一条查询语句中包括有多个函数,函数的作用主要是主组合字符串.两个数据查询出来花了快一分多钟.


select distinct br.repeal_id,

                to_char(br.create_date, 'yyyy-MM-dd hh24:mi') as create_date,

                pbo.object_alias_name objectName,

                fun_get_Repeal_package(br.repeal_id) packageName,

                br.is_audit,

                fun_get_Repeal_CompanyName(br.repeal_id) companyName,

                (select user_name

                   from sm_t_user

                  where user_id = br.create_user_id) user_name,

                case br.is_group_report

                  when 2 then

                   '已修改'

                  when 1 then

                   '已导出'

                  when 0 then

                   '未导出'

                end as is_type,

                br.object_number,

                bpd.project_id,

                br.repeal_status,

                (select stu.user_name

                   from be_t_flow_log btf, sm_t_user stu

                  where btf.sub_user_id = stu.user_id

                    and br.repeal_status = btf.billstatus

                    and br.repeal_id = btf.repeal_id

                    and btf.flow_able <> -1

                    and rownum = 1) ruser_name,

                br.create_user_id,

                br.is_group_report,

                case

                  when ((select count(*)

                           from be_t_flow_log btw

                          where btw.flow_able = 0

                            and btw.repeal_id = br.repeal_id

                            and btw.billstatus = 3

                            and btw.audit_status is null

                            and ((select count(*)

                                    from be_t_flow_log btlg

                                   where btlg.flow_able = -1

                                     and btlg.repeal_id = br.repeal_id) > 0)) > 0) then

                   'red'

                  else

                   ''

                end as color,

                'a' indexNo,

                br.blank_out,

                fun_get_Repeal_Reason(br.repeal_id) repeal_reason

  from be_t_repeal         br,

       be_t_repeal_package brp,

       be_t_project_detail bpd,

       pa_t_bid_package    pbp,

       pa_t_bid_object     pbo,

       be_t_providers      bp,

       be_t_repeal_package btr,

       BE_T_FLOW_LOG       BTFL

 where br.repeal_id = brp.repeal_id

   and brp.project_device_id = bpd.project_device_id

   and bpd.bid_package_id = pbp.bid_package_id

   and pbp.bid_object_id = pbo.bid_object_id

   and brp.provider_id = bp.provider_id

   and br.repeal_id = btr.repeal_id

   and br.repeal_id = btfl.repeal_id

   and btfl.flow_type = 1

   and br.project_id = 990000000000003243;


查询的两条数据结果:

1 990000000000001741 2008-04-14 18:16 铁塔 包1  阿塔其大一互电器有限公司、安徽宏鼎互感器有限公司、安徽宏源电力铁塔制造股份合作公司、安徽宏源线路器材有限公司、安徽明都电力线缆有限公司、鞍山铁塔制造总厂、鞍山万事达电力有限公司、宝丰电缆有限公司、宝鸡铁塔厂、宝胜普睿司曼电缆有限公司、保定保菱变压器有限公司、保定电力修造厂、保定市华北电力线材制造有限公司、保定天威保变电气股份有限公司、保定天威互感器有限公司、北京ABB高压开关设备有限公司、北京北开电气股份有限公司、北京电力设备总厂、北京电研华源电力技术有限公司、北京宏达日新电机有限公司、北京华美煜力电力技术有限公司、北京潞电电气设备有限公司、北京送变电公司线路器材厂、长春聚德龙铁塔集团有限公司、长春三鼎变压器有限公司、常州东芝变压器有限公司、常州华银电线电缆有限公司、常州市武进恒通金属钢丝有限公司、成都双星变压器有限公司、重庆ABB变压器有限公司、重庆市江津电力线路构件厂、重庆顺泰铁塔制造有限公司、重庆渝能泰山电线电缆有限公司、传奇电气(沈阳)有限公司、大连电瓷有限公司、大连互感器有限公司、东莞市高能实业有限公司、恩基客(上海)商贸有限公司、菲尔普斯.道奇(烟台)电缆有限公司、福建德和铁塔设备制造有限公司、福建电建杆塔制造有限责任公司、福建南平太阳电缆股份有限公司、抚顺电瓷制造有限公司、甘肃长通电缆科技股份有限公司、甘肃诚信电线电缆有限责任公司、赣州联盛电力器材有限公司、广东新亚光电缆实业有限公司、广州华盛避雷器实业有限公司、广州市迈克林电力有限公司、广州维奥伊林变压器有限公司 超级管理员 未导出 T120007 990000000000003243 1 超级管理员 990000000000000001 0  a 1 产品型式不满足要求

2 990000000000001761 2008-04-15 11:54 铁塔 包2、包4、包5、包6、包7、包8、包9、包10、包11、包12、包13、包14、包15、包16、包17、包18、包20、包22、包23、包24、包25、包26、包27、包28、包29、包30、包31、包33、包34、包35、包36、包37、包38、包39、包40、包41、包42、包43、包44、包45、包46、包48、包49、包50、包51、包52、包53、包54、包55、包56、包57、包58、包59、包60、包61、包62、包63、包64、包65、包66、包67、包68、包69  重庆顺泰铁塔制造有限公司 EP46 未导出 T120008 990000000000003243 1 EP46 990000000000006222 0  a 1 投标人资格条件不满足要求(缺少鉴定证书或许可证)

两条数据花了61.016秒.

问题主要在几个组合字符串的函数上.

取审批单包名的函数.
create or replace function fun_get_Repeal_package(p_repeal_id varchar)
  return varchar as
  cursor cur is
    select distinct pbp.package_name, pbp.package_order, pbp.child_order
      from be_t_repeal         br,
           be_t_repeal_package brp,
           be_t_project_detail bpd,
           pa_t_bid_package    pbp pa_t_bid_object pbo,
     where br.repeal_id = brp.repeal_id
       and brp.project_device_id = bpd.project_device_id
       and bpd.bid_package_id = pbp.bid_package_id
       and pbp.bid_object_id = pbo.bid_object_id
       and brp.provider_id = bp.provider_id
       and br.repeal_id = p_repeal_id
     order by pbp.package_order || pbp.child_order,
              pbp.child_order,
              pbp.package_name;
  v_package varchar2(1000);
begin
  v_package := '';
  for cur_repeal in cur loop
    v_package := v_package || cur_repeal.package_name || '、';
  end loop;
  v_package := substr(v_package, 1, length(v_package) - 1);
  return v_package;
end;


 

一个审批单对应的包数据比较多是,查询出来数据非常慢.

获得审批单对应供应商函数

create or replace function fun_get_Repeal_CompanyName(p_repeal_id varchar)

  return varchar as

  cursor cur is

    select distinct bp.company_fullname

      from be_t_repeal         br,

           be_t_repeal_package brp,

           be_t_project_detail bpd, 

           be_t_providers bp

     where br.repeal_id = brp.repeal_id

       and brp.project_device_id = bpd.project_device_id 

       and brp.provider_id = bp.provider_id

       and br.repeal_id = p_repeal_id

     order by nlssort(bp.company_fullname, 'NLS_SORT=SCHINESE_PINYIN_M');

  v_company_name varchar2(1000);

begin

  v_company_name := '';

  for cur_repeal in cur loop

    v_company_name := v_company_name || cur_repeal.company_fullname || '、';

  end loop;

  v_company_name := substr(v_company_name, 1, length(v_company_name) - 1);

  return v_company_name;

end;

得到审批单废标原因函数

create or replace function fun_get_Repeal_Reason(p_repeal_id varchar)

  return varchar as



  v_reason_conten varchar(1000);



  cursor cur is

    select distinct bt.reason_content

      from be_t_repeal_reson bt, be_t_repeal_package btrp

     where bt.repeal_reason_id = btrp.repeal_reason_id

       and btrp.repeal_id = p_repeal_id;

begin



   v_reason_conten:='';

    for cur_re in cur loop

      v_reason_conten:=v_reason_conten||cur_re.reason_content||'、';

    end loop;



     v_reason_conten := substr(v_reason_conten, 1, length(v_reason_conten) - 1);



  return   v_reason_conten;

end;







正常情况下直接查询这几个函数非常快.

select fun_get_repeal_companyname(990000000000001761) from dual;



select fun_get_repeal_package(990000000000001761) from dual;


第一条查询只花费时间0.016秒,第二条数据,花费0.015秒


当我去掉这几个函数时同形个查询语只用了0.19秒.

能提供的点优化思路吗?谢谢各位了
   发表时间:2008-04-16  
看上去这么乱呢,你的分析还不够,我还是懒得仔细看了
1 请登录后投票
   发表时间:2008-04-16  
先整形

SELECT DISTINCT 
    BR.REPEAL_ID,
    TO_CHAR(BR.CREATE_DATE,'yyyy-MM-dd hh24:mi') AS CREATE_DATE,
    PBO.OBJECT_ALIAS_NAME OBJECTNAME,
    FUN_GET_REPEAL_PACKAGE(BR.REPEAL_ID) PACKAGENAME,
    BR.IS_AUDIT,
    FUN_GET_REPEAL_COMPANYNAME(BR.REPEAL_ID) COMPANYNAME,
    (
        SELECT USER_NAME
        FROM SM_T_USER
        WHERE USER_ID = BR.CREATE_USER_ID
    ) USER_NAME,
    CASE BR.IS_GROUP_REPORT 
        WHEN 2 THEN 'AAA' 
        WHEN 1 THEN 'BBB' 
        WHEN 0 THEN 'CCC' 
    END AS IS_TYPE,
    BR.OBJECT_NUMBER,
    BPD.PROJECT_ID,
    BR.REPEAL_STATUS,
    (
        SELECT
            STU.USER_NAME
        FROM
        BE_T_FLOW_LOG BTF,
        SM_T_USER STU
        WHERE BTF.SUB_USER_ID = STU.USER_ID
        AND BR.REPEAL_STATUS = BTF.BILLSTATUS
        AND BR.REPEAL_ID = BTF.REPEAL_ID
        AND BTF.FLOW_ABLE <> -1
        AND ROWNUM = 1
    ) RUSER_NAME,
    BR.CREATE_USER_ID,
    BR.IS_GROUP_REPORT,
    CASE 
        WHEN
        (
            (
                SELECT COUNT(*)
                FROM
                BE_T_FLOW_LOG BTW
                WHERE BTW.FLOW_ABLE = 0
                    AND BTW.REPEAL_ID = BR.REPEAL_ID
                    AND BTW.BILLSTATUS = 3
                    AND BTW.AUDIT_STATUS IS NULL
                    AND 
                    (
                        (
                            SELECT COUNT(*)
                            FROM BE_T_FLOW_LOG BTLG
                            WHERE BTLG.FLOW_ABLE = -1
                                AND BTLG.REPEAL_ID = BR.REPEAL_ID
                        ) > 0
                    )
            ) > 0
        ) THEN 'red' 
        ELSE '' 
    END AS COLOR,
    'a' INDEXNO,
    BR.BLANK_OUT,
    FUN_GET_REPEAL_REASON(BR.REPEAL_ID) REPEAL_REASON
FROM
    BE_T_REPEAL BR,
    BE_T_REPEAL_PACKAGE BRP,
    BE_T_PROJECT_DETAIL BPD,
    PA_T_BID_PACKAGE PBP,
    PA_T_BID_OBJECT PBO,
    BE_T_PROVIDERS BP,
    BE_T_REPEAL_PACKAGE BTR,
    BE_T_FLOW_LOG BTFL
WHERE BR.REPEAL_ID = BRP.REPEAL_ID
    AND BRP.PROJECT_DEVICE_ID = BPD.PROJECT_DEVICE_ID
    AND BPD.BID_PACKAGE_ID = PBP.BID_PACKAGE_ID
    AND PBP.BID_OBJECT_ID = PBO.BID_OBJECT_ID
    AND BRP.PROVIDER_ID = BP.PROVIDER_ID
    AND BR.REPEAL_ID = BTR.REPEAL_ID
    AND BR.REPEAL_ID = BTFL.REPEAL_ID
    AND BTFL.FLOW_TYPE = 1
    AND BR.PROJECT_ID = 990000000000003243


create or replace function fun_get_Repeal_package(
    p_repeal_id varchar
)  return varchar
as
    cursor cur 
    is
        SELECT
            DISTINCT PBP.PACKAGE_NAME,
            PBP.PACKAGE_ORDER,
            PBP.CHILD_ORDER
        FROM
            BE_T_REPEAL BR,
            BE_T_REPEAL_PACKAGE BRP,
            BE_T_PROJECT_DETAIL BPD,
            PA_T_BID_PACKAGE PBP,
            PA_T_BID_OBJECT PBO,
            BE_T_PROVIDERS BP
        WHERE BR.REPEAL_ID = BRP.REPEAL_ID
            AND BRP.PROJECT_DEVICE_ID = BPD.PROJECT_DEVICE_ID
            AND BPD.BID_PACKAGE_ID = PBP.BID_PACKAGE_ID
            AND PBP.BID_OBJECT_ID = PBO.BID_OBJECT_ID
            AND BRP.PROVIDER_ID = BP.PROVIDER_ID
            AND BR.REPEAL_ID = P_REPEAL_ID
        ORDER BY 
            PBP.PACKAGE_ORDER||PBP.CHILD_ORDER,
            PBP.CHILD_ORDER,
            PBP.PACKAGE_NAME
    ;

    v_package varchar2(1000);
    begin  v_package := '';
    for cur_repeal in cur
    loop
        v_package := v_package || cur_repeal.package_name || '、';
    end loop;
    v_package := substr(v_package, 1, length(v_package) - 1);
    return v_package;
end;



这样看就容易多了

问题的原因很简单
因为FUN_GET_REPEAL_PACKAGE、FUN_GET_REPEAL_COMPANYNAME函数的做法有性能问题
在查询语句中调用它就相当于子查询

解决办法有两个,
1、把原来的sql语句改写成非distinct的形式,把字符串拼接处理放到java程序中
2、用oracle自定义聚集函数的形式改写FUN_GET_REPEAL_PACKAGE、FUN_GET_REPEAL_COMPANYNAME函数
  把distinct形式的sql改写成group形式即可

改写函数的时候,请参照下面的函数实现

--自定義集約関数→SORTED

CREATE OR REPLACE TYPE VcArrayType IS TABLE OF VARCHAR2(4000);
/

CREATE OR REPLACE TYPE STR_CONN_TYPE AS OBJECT
(
    --vStr VARCHAR2(4000),
    vStr VcArrayType,

    STATIC FUNCTION
    ODCIAggregateInitialize(sctx IN OUT STR_CONN_TYPE )
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateIterate(SELF IN OUT STR_CONN_TYPE ,
        VALUE IN VARCHAR2 )
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateMerge(SELF IN OUT STR_CONN_TYPE,
        ctx2 IN STR_CONN_TYPE)
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateTerminate(SELF IN STR_CONN_TYPE,
        returnValue OUT VARCHAR2,
        flags IN NUMBER)
    RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY STR_CONN_TYPE
IS
    STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT STR_CONN_TYPE)
    RETURN NUMBER
    IS
    BEGIN
        --sctx := STR_CONN_TYPE(NULL);
        sctx := STR_CONN_TYPE(VcArrayType());
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateIterate(SELF IN OUT STR_CONN_TYPE,
    VALUE IN VARCHAR2 )
    RETURN NUMBER
    IS
    BEGIN
        --SELF.vStr := SELF.vStr || ';' || VALUE;
        vStr.EXTEND;
        vStr(vStr.COUNT) := VALUE;
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateMerge(SELF IN OUT STR_CONN_TYPE,
        ctx2 IN STR_CONN_TYPE)
    RETURN NUMBER
    IS
    BEGIN
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateTerminate(SELF IN STR_CONN_TYPE,
        returnValue OUT VARCHAR2,
        flags IN NUMBER)
    RETURN NUMBER
    IS
        tmp_vStr VARCHAR2(4000);
    BEGIN
        --returnValue := LTRIM(SELF.vStr,';');
        FOR rec_Value IN (
            SELECT column_value 
            FROM TABLE(vStr) 
            --ORDER BY to_number(column_value)
            ORDER BY column_value
        ) 
        LOOP
            --tmp_vStr := tmp_vStr || '_' || rec_Value.column_value;
            tmp_vStr := tmp_vStr || '、' || rec_Value.column_value;
        END LOOP;
        --returnValue := LTRIM(tmp_vStr,'_');
        returnValue := LTRIM(tmp_vStr,'、');
        RETURN ODCIConst.Success;
    END;
END;
/

CREATE OR REPLACE FUNCTION ConnStr(input VARCHAR2)
RETURN VARCHAR2
PARALLEL_ENABLE AGGREGATE USING STR_CONN_TYPE;
/

0 请登录后投票
   发表时间:2008-04-16  
语句太长了...

找到了解决方法.但这个问题不是知道可能是oracle的查询语句问题.

我在函数fun_get_Repeal_package中加入一些输出信息如:
create or replace function fun_get_Repeal_package(p_repeal_id varchar)
  return varchar as
  cursor cur is
    select distinct pbp.package_name, pbp.package_order, pbp.child_order
      from be_t_repeal         br,
           be_t_repeal_package brp,
           be_t_project_detail bpd,
           pa_t_bid_package    pbp pa_t_bid_object pbo,
    --be_t_providers      bp
     where br.repeal_id = brp.repeal_id
       and brp.project_device_id = bpd.project_device_id
       and bpd.bid_package_id = pbp.bid_package_id
       and pbp.bid_object_id = pbo.bid_object_id
       and brp.provider_id = bp.provider_id
       and br.repeal_id = p_repeal_id
     order by pbp.package_order || pbp.child_order,
              pbp.child_order,
              pbp.package_name;
  v_package varchar2(1000);
begin
  v_package := '';
  dbms_output.put_line(to_char(new_time(sysdate, 'PDT', 'GMT'),
                               'yyyy.mm.dd hh24:mi:ss'));
  for cur_repeal in cur loop
    v_package := v_package || cur_repeal.package_name || '、';
  end loop;
--输出两这函数的执行的时间
  dbms_output.put_line(to_char(new_time(sysdate, 'PDT', 'GMT'),
                               'yyyy.mm.dd hh24:mi:ss'));
  v_package := substr(v_package, 1, length(v_package) - 1);
  return v_package;
end;
 

这个函数直接放在查询语中时执行非常慢.
output结果:
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43
2008.04.16 16:54:43[align=left][/align]
2008.04.16 16:54:43
2008.04.16 16:54:43

....
其输出信息已经超出我设置的output buffer size 26000的大小
并且报0ra-200:ORM-10027 :buffer overflow,limit of 26000 bytes
ORA-06512: 在"SYS.DBMS_OUTPUT" ,line 32
ORA-06512: 在"SYS.DBMS_OUTPUT" ,line 97
ORA-06512: 在"SYS.DBMS_OUTPUT" ,line 112

只有两条数据怎么会执行哪么多.真是奇怪.我个人认为,可能是有可能在与其它每个表关联时他会产生许多重复的数据,大量的重复数据每次都会去执行哪几个函数.
为了证实我的这个想法.我同样的两条数据使用单表执行.
 select l.*,fun_get_repeal_package(l.repeal_id),fun_get_repeal_companyname(l.repeal_id) from be_t_repeal l where l.repeal_id in (990000000000001761,990000000000001741)


测试结果0.063秒就完成
其结果并不慢,原因所在找到了.问题就有解决的方法:
在Sql语句最后一层外包一层.
  select tt.* ,fun_get_repeal_package(tt.repeal_id),fun_get_repeal_companyname(tt.repeal_id),fun_get_repeal_reason(tt.repeal_id) from 上面复杂的sql去掉里面几个函数
) tt



如果大家有遇到同样的问题可以这样解决.
呵呵,超长的语句.

0 请登录后投票
   发表时间:2008-04-16  
to:armorking
非常感谢你的建议.


请问你是用的pl/sql吗?
能否告诉我pl/sql的代码格式的怎么设置,谢谢!

关于你提出的哪个方案,我也想过.主要这个也是跟我们使用的平台有关.我使用的powerbos平台,这个平台在可以直接在写sql语句就可以实现所有功能.增删改查.基本上不需要写一点代码.呵呵,但平台还有些缺点.复杂的业务还是得自己写代码.
0 请登录后投票
   发表时间:2008-04-16  
chenhj520 写道
to:armorking
非常感谢你的建议.


请问你是用的pl/sql吗?
能否告诉我pl/sql的代码格式的怎么设置,谢谢!

关于你提出的哪个方案,我也想过.主要这个也是跟我们使用的平台有关.我使用的powerbos平台,这个平台在可以直接在写sql语句就可以实现所有功能.增删改查.基本上不需要写一点代码.呵呵,但平台还有些缺点.复杂的业务还是得自己写代码.



那个自定义聚集函数,是用pl/sql写的,
可以在group by 语句中直接把它当作SUM,MIN之类的函数一样使用
我曾经测试过,应该是好使的

至于pl/sql代码的格式设置
我是做了一个sql整形用的java函数

/*
 * @(#) SQLStringFormatter.java 2005-09-14
 */
package org.ibatis.helper.common.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.ibatis.helper.common.util.ConstantMark;
import org.ibatis.helper.common.util.StringCheckUtil;


/**
 * SQL文の書式変換用
 * @author LIANGZHI ZHU
 * @since   JDK1.4
 * $Date: 2005-09-14 13:01:33 $
 */
public class SQLStringFormatter 
{

    private static Class clazz = SQLStringFormatter.class;

    public static String formatSQLString(String sqlString)
    {
        String strSrc = toUpperCaseExceptConst(sqlString);

        strSrc = strSrc.replaceAll("\t"         , "    ");
        strSrc = strSrc.replaceAll("SELECT "    , "SELECT \n");
        strSrc = strSrc.replaceAll(" \\("       , " \n\\(\n ");
        strSrc = strSrc.replaceAll(" \\) "      , " \n\\) \n");
        strSrc = strSrc.replaceAll(","          , ", \n");
        strSrc = strSrc.replaceAll(" VALUES "   , "\nVALUES \n");
        strSrc = strSrc.replaceAll(" FROM "     , "\nFROM \n");
        strSrc = strSrc.replaceAll(" SET "      , "\nSET \n");
        strSrc = strSrc.replaceAll(" LEFT "     , "\nLEFT ");
        strSrc = strSrc.replaceAll(" INNER "    , "\nINNER ");
        strSrc = strSrc.replaceAll(" ON "       , " ON\n");
        strSrc = strSrc.replaceAll(" WHERE "    , "\nWHERE ");
        strSrc = strSrc.replaceAll(" AND "      , "\nAND ");
        strSrc = strSrc.replaceAll(" OR "       , "\nOR ");
        strSrc = strSrc.replaceAll(" UNION "    , "\nUNION\n");
        strSrc = strSrc.replaceAll(" ORDER "    , "\nORDER ");
        strSrc = strSrc.replaceAll(" GROUP "    , "\nGROUP ");
        //strSrc = strSrc.replaceAll(" BY "     , " BY \n");
        strSrc = strSrc.replaceAll("\n\n"       , "\n");

//StepTimeStampUtil.logTimeStamp(clazz);

        Stack stack = new Stack();

        boolean additionalIndentFlg = false;
        StringBuffer sb = new StringBuffer(strSrc.length());
        String[] lineArray = strSrc.split("\n");
        for (int i = 0 ; i < lineArray.length ; i++)
        {
            String line = lineArray[i];
            if (line == null
            ){
                continue;
            }
            line = line.trim();
            if (line.length() == 0
            ){
                continue;
            }

            int level = stack.size();

            if (line.indexOf("(") != -1
            ){
                stack.push("(");
            }

            int indexRight = line.indexOf(")");
            if (indexRight != -1
            ){
                stack.pop();
                if (indexRight == 0
                ){
                    level--;
                }
            }

            if (containsKeyword(line, "FROM")
            ){
                additionalIndentFlg = false;
            }

            if (additionalIndentFlg
            ){
                level++;
            }

            sb.append(createIndentByLevel(level));
            sb.append(line).append("\n");

            if (containsKeyword(line, "SELECT")
            ){
                additionalIndentFlg = true;
            }

        }

//StepTimeStampUtil.logTimeStamp(clazz);

        return sb.toString();
    }


    //Set<String 分割マーク>
    private static Set DIVISION_MARK_SET;

    static 
    {
        DIVISION_MARK_SET = new HashSet();
        DIVISION_MARK_SET.add(" ");
        DIVISION_MARK_SET.add("(");
        DIVISION_MARK_SET.add(")");
        DIVISION_MARK_SET.add("\t");
        DIVISION_MARK_SET.add("\r");
        DIVISION_MARK_SET.add("\n");
    }

    private static boolean containsKeyword(String str, String keyword)
    {
        if (StringCheckUtil.isEmpty(keyword)
            || StringCheckUtil.isEmpty(str)
        ){
            return false;
        }

        int index = str.indexOf(keyword);
        if (index == -1
        ){
            return false;
        }

        if (index > 0
            && !DIVISION_MARK_SET.contains(String.valueOf(str.charAt(index - 1)))
        ){
            return false;
        }

        int keywordLength = keyword.length();

        if (index < str.length() - 1 - keywordLength
            && !DIVISION_MARK_SET.contains(String.valueOf(str.charAt(index + keywordLength + 1)))
        ){
            return false;
        }

        return true;
    }


    //Map<Integer level, String indentStr>
    private static Map INDENT_MAP = new HashMap();
    private static String createIndentByLevel(int levelValue)
    {
        Integer level = Integer.valueOf(String.valueOf(levelValue));
        if (INDENT_MAP.containsKey(level)
        ){
            return (String)INDENT_MAP.get(level);
        }

        //String indentStr = StringUtils.leftPad("", level.intValue() * 4, ConstantMark.CHAR_SPACE);
        int len = level.intValue() * 4;
        StringBuffer sb = new StringBuffer(len);
        for (int i = 0 ; i < len ; i++)
        {
            sb.append(ConstantMark.SPACE_CHAR);
        }
        String indentStr = sb.toString();

        synchronized(clazz)
        {
            INDENT_MAP.put(level, indentStr);
        }

        return indentStr;
    }

    private static String toUpperCaseExceptConst(String str)
    {
        if (StringCheckUtil.isTrimedEmpty(str)
        ){
            return str;
        }

        StringBuffer sb = new StringBuffer();
        String[] strArray = str.split(ConstantMark.SINGLE_QUOTAION);
        for (int i = 0 ; i < strArray.length ; i++)
        {
            sb.append((i > 0) ? ConstantMark.SINGLE_QUOTAION : ConstantMark.BLANK);
            String line = StringCheckUtil.nvl(strArray[i]);
            if ((i % 2 == 0)
            ){
                line = trimSpace2One(line.toUpperCase());
            }
            sb.append(line);
        }
        return sb.toString();
    }

    private static String trimSpace2One(String str)
    {
        if (StringCheckUtil.isEmpty(str)
        ){
            return str;
        }

        StringBuffer sb = new StringBuffer();
        char[] charArray = str.toCharArray();
        boolean spaceStartFlg = false;
        for (int i = 0 ; i < charArray.length ; i++)
        {
            if (charArray[i] == ConstantMark.SPACE_CHAR
                || charArray[i] == ConstantMark.FULL_SPACE_CHAR
                || charArray[i] == ConstantMark.TAB_CHAR
            ){
                spaceStartFlg = true;
                continue;
            }
            if (spaceStartFlg
            ){
                sb.append(ConstantMark.SPACE_CHAR);
                spaceStartFlg = false;
            }
            sb.append(charArray[i]);
        }
        if (spaceStartFlg
        ){
            sb.append(ConstantMark.SPACE_CHAR);
        }
        return sb.toString();
    }

    public static void main(String[] args) 
    {
        String sqlString = "select distinct pbp.package_name,pbp.package_order,pbp.child_order      from be_t_repeal         br,           be_t_repeal_package brp,           be_t_project_detail bpd,           pa_t_bid_package    pbp,           pa_t_bid_object     pbo,           be_t_providers      bp     where br.repeal_id = brp.repeal_id       and brp.project_device_id = bpd.project_device_id       and bpd.bid_package_id = pbp.bid_package_id       and pbp.bid_object_id = pbo.bid_object_id       and brp.provider_id = bp.provider_id       and br.repeal_id = p_repeal_id     order by pbp.package_order||pbp.child_order,pbp.child_order,pbp.package_name";
        System.out.println(formatSQLString(sqlString));
    }
}


package org.ibatis.helper.common.util;

/**
 * @author zhu liangzhi
 * @date   2007/03/07
 *
 * Mark Contants
 */
public class ConstantMark 
{
    private ConstantMark(){} //private contructor

    public static final String BLANK                = "";

    public static final String PERIOD               = ".";

    public static final String COLON                = ":";

    public static final String SEMICOLON            = ";";

    public static final String SHARP                = "#";

    public static final String DOLLAR               = "$";

    public static final String UNDER_BAR            = "_";

    public static final String TAB                  = "\t";

    public static final String LINE_FEED            = "\n";

    public static final String SLASH                = "/";

    public static final String SPACE                = " ";

    public static final String SINGLE_QUOTAION      = "'";


    //丸括弧
    public static final String ROUND_BRACKET_LEFT   = "(";
    public static final String ROUND_BRACKET_RIGHT  = ")";

    //山形括弧
    public static final String ANGLE_BRACKET_LEFT   = "<";
    public static final String ANGLE_BRACKET_RIGHT  = ">";

    //中括弧
    public static final String BRACE_LEFT           = "{";
    public static final String BRACE_RIGHT          = "}";


    public static final char SLASH_CHAR             = '/';

    public static final char SPACE_CHAR             = ' ';

    public static final char FULL_SPACE_CHAR        = ' ';

    public static final char TAB_CHAR               = '\t';

    //シングルクォーテーション
    public static final char SINGLE_QUOTAION_CHAR   = '\'';

}


/*
 * 作成日: 2007/03/23
 */
package org.ibatis.helper.common.util;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * @author zhu liangzhi
 *
 * Stringに関するチェック処理用
 */
public class StringCheckUtil 
{
    //////////////////////////////////////////////////////////
    //Properties

    private static Set BOOLEAN_VALUE_SET;


    //////////////////////////////////////////////////////////
    //static

    static 
    {
        BOOLEAN_VALUE_SET = new HashSet();
        BOOLEAN_VALUE_SET.add("true");
        BOOLEAN_VALUE_SET.add("false");
    }


    //////////////////////////////////////////////////////////
    //public static method

    public static boolean isNull(String str)
    {
        return str == null;
    }

    public static boolean isEmpty(String str)
    {
        return isNull(str) || str.length() == 0;
    }

    public static boolean isTrimedEmpty(String str)
    {
        return isNull(str) || str.trim().length() == 0;
    }

    public static String nvl(String str)
    {
        return isNull(str) ? "" : str;
    }

    public static boolean isBooleanType(String str)
    {
        return !isEmpty(str) 
            && BOOLEAN_VALUE_SET.contains(str.toLowerCase())
        ;
    }

    public static boolean isPermitted(Set permittedValueSet, String value)
    {
        if (value == null
            || value.length() == 0
            || permittedValueSet == null
            || permittedValueSet.size() == 0
        ){
            return true;
        }

        int limit = 0;
        for (Iterator i = permittedValueSet.iterator() ; i.hasNext() ; )
        {
            String permittedValue = (String)i.next();
            int permittedValueLength = (permittedValue == null) 
                ? 0 : permittedValue.length();
            limit = (permittedValueLength > limit) 
                ? permittedValueLength : limit;
        }

        if (limit == 0
        ){
            return true;
        }

        value = value.toUpperCase();
        int valueLength = value.length();

        StringBuffer sb = new StringBuffer();
        for (int i = 0 ; i < valueLength ; i++)
        {
            sb.append(value.charAt(i));
            if (permittedValueSet.contains(sb.toString())
            ){
                sb = new StringBuffer();
                continue;
            }
            if (sb.length() >= limit
                || i == valueLength - 1
            ){
                return false;
            }
        }
        return true;

    }
}


不过这个sql整形程序还不是很完善,有时候还需要在生成结果的基础上作些调整
1 请登录后投票
   发表时间:2008-04-16  
    to:  armorking

那个自定义聚集函数,是用pl/sql写的,
可以在group by 语句中直接把它当作SUM,MIN之类的函数一样使用
我曾经测试过,应该是好使的

我使用别一种方法把问题解决了.
根据你提交的聚集函数的方法.

测试时去掉多余的字段

select distinct br.repeal_id, 
           ConnStr(pbp.package_name) 
          from be_t_repeal         br,
               be_t_repeal_package brp,
               be_t_project_detail bpd,
               pa_t_bid_package    pbp,
               pa_t_bid_object     pbo,
               be_t_providers      bp,
               be_t_repeal_package btr,
               BE_T_FLOW_LOG       BTFL
         where br.repeal_id = brp.repeal_id
           and brp.project_device_id = bpd.project_device_id
           and bpd.bid_package_id = pbp.bid_package_id
           and pbp.bid_object_id = pbo.bid_object_id
           and brp.provider_id = bp.provider_id
           and br.repeal_id = btr.repeal_id
           and br.repeal_id = btfl.repeal_id
           and btfl.flow_type = 1
           and br.project_id = 990000000000003243           
       group by br.repeal_id  


我在执行语句测试时,一个审批单对应有63个包.使用分组和自定义的聚函数的产生的
ORA-06052:PL/SQL :数据字或值错误:字符串缓冲区太小.
ORA-O6512:在BID.STR_CONN_TYPE,line 47
在47行代码是
      tmp_vStr := tmp_vStr || '、' || rec_Value.column_value;

tmp_vStr VARCHAR2(4000)定义为最大值.

其聚集函数为你提供的函数.

0 请登录后投票
   发表时间:2008-04-17  
chenhj520 写道


我在执行语句测试时,一个审批单对应有63个包.使用分组和自定义的聚函数的产生的
ORA-06052:PL/SQL :数据字或值错误:字符串缓冲区太小.
ORA-O6512:在BID.STR_CONN_TYPE,line 47
在47行代码是
      tmp_vStr := tmp_vStr || '、' || rec_Value.column_value;

tmp_vStr VARCHAR2(4000)定义为最大值.

其聚集函数为你提供的函数.



我测试了一下,当字符串长度超出4000的时候,是会发生错误

在oracle函数中,允许定义的字符串最大长度是32767
所以把函数中定义的VcArrayType改成VARCHAR2(32767)
并且把tmp_vStr 类型声明为VARCHAR2(32767)的时候
      tmp_vStr := tmp_vStr || '、' || rec_Value.column_value;

这个字符串拼接处理就不会出错了

但是,当把这个字符串作为varchar或者是varchar2类型的返回值来使用的时候,最大长度仍然不能超过4000

所以,为了避免出错,在返回值之前必须作substr处理

如下:
--自定義集約関数→SORTED
DROP FUNCTION ConnStr
/
DROP TYPE BODY STR_CONN_TYPE
/
DROP TYPE STR_CONN_TYPE
/
DROP TYPE VcArrayType
/

CREATE OR REPLACE TYPE VcArrayType IS TABLE OF VARCHAR2(32767);
/

CREATE OR REPLACE TYPE STR_CONN_TYPE AS OBJECT
(
    --vStr VARCHAR2(4000),
    vStr VcArrayType,

    STATIC FUNCTION
    ODCIAggregateInitialize(sctx IN OUT STR_CONN_TYPE )
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateIterate(SELF IN OUT STR_CONN_TYPE ,
        VALUE IN VARCHAR2 )
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateMerge(SELF IN OUT STR_CONN_TYPE,
        ctx2 IN STR_CONN_TYPE)
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateTerminate(SELF IN STR_CONN_TYPE,
        returnValue OUT VARCHAR2,
        flags IN NUMBER)
    RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY STR_CONN_TYPE
IS
    STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT STR_CONN_TYPE)
    RETURN NUMBER
    IS
    BEGIN
        --sctx := STR_CONN_TYPE(NULL);
        sctx := STR_CONN_TYPE(VcArrayType());
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateIterate(SELF IN OUT STR_CONN_TYPE,
    VALUE IN VARCHAR2 )
    RETURN NUMBER
    IS
    BEGIN
        --SELF.vStr := SELF.vStr || ';' || VALUE;
        vStr.EXTEND;
        vStr(vStr.COUNT) := VALUE;
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateMerge(SELF IN OUT STR_CONN_TYPE,
        ctx2 IN STR_CONN_TYPE)
    RETURN NUMBER
    IS
    BEGIN
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateTerminate(SELF IN STR_CONN_TYPE,
        returnValue OUT VARCHAR2,
        flags IN NUMBER)
    RETURN NUMBER
    IS
        tmp_vStr VARCHAR2(32767);
    BEGIN
        --returnValue := LTRIM(SELF.vStr,';');
        FOR rec_Value IN (
            SELECT column_value 
            FROM TABLE(vStr) 
            --ORDER BY to_number(column_value)
            ORDER BY column_value
        ) 
        LOOP
            --tmp_vStr := tmp_vStr || '_' || rec_Value.column_value;
            tmp_vStr := tmp_vStr || '、' || rec_Value.column_value;
        END LOOP;
        --returnValue := LTRIM(tmp_vStr,'_');
        --returnValue := LTRIM(tmp_vStr,'、');
        returnValue := SUBSTRB(LTRIM(tmp_vStr, '、'), 1, 4000);
        RETURN ODCIConst.Success;
    END;
END;
/

CREATE OR REPLACE FUNCTION ConnStr(input VARCHAR2)
RETURN VARCHAR2
PARALLEL_ENABLE AGGREGATE USING STR_CONN_TYPE;
/


当然,这样一来,在实际数据长度超过4000的时候,就得不到业务需求的结果了


我也测试了原先的fun_get_Repeal_CompanyName的做法
基于同样的原因,这种做法在在实际数据长度超过4000的时候也是会报错的

我想,在使用varchar型的返回值的函数实现这个业务的时候,这种现象是不可避免的

所以,oracle的解决办法可能就是使用clob类型的返回值来实现这个函数
这一点目前还尚待证实
不过,使用clob返回值的话,java端取数据的时候就要麻烦不少了
0 请登录后投票
   发表时间:2008-04-17  
用CLOB类型作返回值,刚才的问题得到解决
于是写了两个函数:CONNSTR_TO_VARCHAR和CONNSTR_TO_CLOB
分别处理VARCHAR与CLOB返回值


--自定義集約関数→SORTED
DROP FUNCTION CONNSTR_TO_VARCHAR
/
DROP TYPE BODY VARCHAR_STR_CONN_TYPE
/
DROP TYPE VARCHAR_STR_CONN_TYPE
/
DROP FUNCTION CONNSTR_TO_CLOB
/
DROP TYPE BODY CLOB_STR_CONN_TYPE
/
DROP TYPE CLOB_STR_CONN_TYPE
/
DROP TYPE VcArrayType
/

CREATE OR REPLACE TYPE VcArrayType IS TABLE OF VARCHAR2(4000);
/

-------------------------------------------
--1.CONNSTR_TO_VARCHAR

CREATE OR REPLACE TYPE VARCHAR_STR_CONN_TYPE AS OBJECT
(
    --vStr VARCHAR2(4000),
    vStr VcArrayType,

    STATIC FUNCTION
    ODCIAggregateInitialize(sctx IN OUT VARCHAR_STR_CONN_TYPE )
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateIterate(SELF IN OUT VARCHAR_STR_CONN_TYPE ,
        VALUE IN VARCHAR2 )
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateMerge(SELF IN OUT VARCHAR_STR_CONN_TYPE,
        ctx2 IN VARCHAR_STR_CONN_TYPE)
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateTerminate(SELF IN VARCHAR_STR_CONN_TYPE,
        returnValue OUT VARCHAR2,
        flags IN NUMBER)
    RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY VARCHAR_STR_CONN_TYPE
IS
    STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT VARCHAR_STR_CONN_TYPE)
    RETURN NUMBER
    IS
    BEGIN
        sctx := VARCHAR_STR_CONN_TYPE(VcArrayType());
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateIterate(SELF IN OUT VARCHAR_STR_CONN_TYPE,
    VALUE IN VARCHAR2 )
    RETURN NUMBER
    IS
    BEGIN
        vStr.EXTEND;
        vStr(vStr.COUNT) := VALUE;
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateMerge(SELF IN OUT VARCHAR_STR_CONN_TYPE,
        ctx2 IN VARCHAR_STR_CONN_TYPE)
    RETURN NUMBER
    IS
    BEGIN
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateTerminate(SELF IN VARCHAR_STR_CONN_TYPE,
        returnValue OUT VARCHAR2,
        flags IN NUMBER)
    RETURN NUMBER
    IS
        tmp_vStr VARCHAR2(4000);
        loop_vstr VARCHAR2(4000);
        sperator VARCHAR2(255);
        sperator_size NUMBER;
        current_size NUMBER;
    BEGIN
        sperator := '、';
        sperator_size := LENGTHB(sperator);
        current_size := 0;

        FOR rec_Value IN (
            SELECT column_value 
            FROM TABLE(vStr) 
            ORDER BY column_value
        ) 
        LOOP
            loop_vstr := rec_Value.column_value;
            current_size := current_size + sperator_size + LENGTHB(loop_vstr);
            IF (current_size <= 4000)
            THEN
                tmp_vStr := tmp_vStr || sperator || loop_vstr;
            END IF;
        END LOOP;

        returnValue := LTRIM(tmp_vStr, sperator);
        RETURN ODCIConst.Success;
    END;
END;
/

CREATE OR REPLACE FUNCTION CONNSTR_TO_VARCHAR(input VARCHAR2)
RETURN VARCHAR2
PARALLEL_ENABLE AGGREGATE USING VARCHAR_STR_CONN_TYPE;
/


-------------------------------------------
--2.CONNSTR_TO_CLOB

CREATE OR REPLACE TYPE CLOB_STR_CONN_TYPE AS OBJECT
(
    --vStr VARCHAR2(4000),
    vStr VcArrayType,

    STATIC FUNCTION
    ODCIAggregateInitialize(sctx IN OUT CLOB_STR_CONN_TYPE )
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateIterate(SELF IN OUT CLOB_STR_CONN_TYPE ,
        VALUE IN VARCHAR2 )
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateMerge(SELF IN OUT CLOB_STR_CONN_TYPE,
        ctx2 IN CLOB_STR_CONN_TYPE)
    RETURN NUMBER,

    MEMBER FUNCTION
    ODCIAggregateTerminate(SELF IN CLOB_STR_CONN_TYPE,
        returnValue OUT CLOB,
        flags IN NUMBER)
    RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY CLOB_STR_CONN_TYPE
IS
    STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT CLOB_STR_CONN_TYPE)
    RETURN NUMBER
    IS
    BEGIN
        sctx := CLOB_STR_CONN_TYPE(VcArrayType());
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateIterate(SELF IN OUT CLOB_STR_CONN_TYPE,
    VALUE IN VARCHAR2 )
    RETURN NUMBER
    IS
    BEGIN
        vStr.EXTEND;
        vStr(vStr.COUNT) := VALUE;
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateMerge(SELF IN OUT CLOB_STR_CONN_TYPE,
        ctx2 IN CLOB_STR_CONN_TYPE)
    RETURN NUMBER
    IS
    BEGIN
        RETURN ODCIConst.Success;
    END;

    MEMBER FUNCTION ODCIAggregateTerminate(SELF IN CLOB_STR_CONN_TYPE,
        returnValue OUT CLOB,
        flags IN NUMBER)
    RETURN NUMBER
    IS
        tmp_vStr VARCHAR2(32767);
        loop_vstr VARCHAR2(4000);
        sperator VARCHAR2(255);
        sperator_size NUMBER;
        is_null_flg NUMBER;
        current_size NUMBER;
    BEGIN
        sperator := '、';
        sperator_size := LENGTHB(sperator);
        is_null_flg := 0;
        current_size := 0;

        FOR rec_Value IN (
            SELECT column_value 
            FROM TABLE(vStr) 
            ORDER BY column_value
        ) 
        LOOP
            loop_vstr := rec_Value.column_value;
            current_size := current_size + sperator_size + LENGTHB(loop_vstr);
            IF (current_size > 32767)
            THEN
                IF (is_null_flg = 1)
                THEN 
                    DBMS_LOB.APPEND(returnValue, TO_CLOB(tmp_vStr));
                END IF;

                IF (is_null_flg = 0)
                THEN 
                    tmp_vStr := LTRIM(tmp_vStr, sperator);
                    returnValue := TO_CLOB(tmp_vStr);
                    is_null_flg := 1;
                END IF;

                tmp_vStr := '';
                current_size := 0;
            END IF;
            tmp_vStr := tmp_vStr || sperator || loop_vstr;
        END LOOP;

        IF (is_null_flg = 1)
        THEN 
            DBMS_LOB.APPEND(returnValue, TO_CLOB(tmp_vStr));
        END IF;

        IF (is_null_flg = 0)
        THEN 
            tmp_vStr := LTRIM(tmp_vStr, sperator);
            returnValue := TO_CLOB(tmp_vStr);
        END IF;

        RETURN ODCIConst.Success;
    END;
END;
/

CREATE OR REPLACE FUNCTION CONNSTR_TO_CLOB(input VARCHAR2)
RETURN CLOB
PARALLEL_ENABLE AGGREGATE USING CLOB_STR_CONN_TYPE;
/
0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics