浏览 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秒. 能提供的点优化思路吗?谢谢各位了 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-04-16
看上去这么乱呢,你的分析还不够,我还是懒得仔细看了
|
|
返回顶楼 | |
发表时间: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; / |
|
返回顶楼 | |
发表时间: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 如果大家有遇到同样的问题可以这样解决. 呵呵,超长的语句. |
|
返回顶楼 | |
发表时间:2008-04-16
to:armorking
非常感谢你的建议. 请问你是用的pl/sql吗? 能否告诉我pl/sql的代码格式的怎么设置,谢谢! 关于你提出的哪个方案,我也想过.主要这个也是跟我们使用的平台有关.我使用的powerbos平台,这个平台在可以直接在写sql语句就可以实现所有功能.增删改查.基本上不需要写一点代码.呵呵,但平台还有些缺点.复杂的业务还是得自己写代码. |
|
返回顶楼 | |
发表时间: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整形程序还不是很完善,有时候还需要在生成结果的基础上作些调整 |
|
返回顶楼 | |
发表时间: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)定义为最大值. 其聚集函数为你提供的函数. |
|
返回顶楼 | |
发表时间: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端取数据的时候就要麻烦不少了 |
|
返回顶楼 | |
发表时间: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; / |
|
返回顶楼 | |