请问各位数据库高手,我在写一个oracle存储过程的时候,有这样的一个需求,用户输入起始日期和结束日期,然后统计出各个公司每个月的销售情况,我是用存储过程写的,根据条件拼成一条sql语句,然后查询返回一个游标结果集,如果输入的起始日期和结束日期差距很大的话,那么动态拼成的sql语句会很长,以致于超过varchar2的最长长度而报错,在网上找了一些资料,说可以用dbms_sql.varchar2s可以解决,但是由于自己对oracle数据库不是很熟悉,弄了半天也没有解决,还请各位帮忙指点
怎么样通过dbms_sql.varchar2s返回一个查询结果游标记录集
怎么样通过dbms_sql.varchar2s返回一个查询结果游标记录集
between and不就可以了,怎么会很长呢
create or replace procedure p_wh_group_month_add_count(
--统计各大类每月新增代码数量结果
in_begin_day in varchar2,
--起始日期(如:2010-08-08)
in_end_day in varchar2,
--结束日期(如:2010-08-08)
v_cur out sys_refcursor --返回游标
--返回各大类每月新增代码数量结果
) is
i NUMBER;v_poor_month NUMBER;
--起始日期和结束日期月份之差
v_begin_month_last_day varchar2(20);
--起始日期的这个月的最后一天
v_end_month_begin_day varchar2(20);
--结束日期的这个月的第一天
v_month_day varchar2(20);
v_temp_first_day DATE;v_sql varchar2(32767) ;
begin
v_temp_first_day:=to_date(in_begin_day,'yyyy-mm-dd');
v_begin_month_last_day:=to_char(last_day(v_temp_first_day),'yyyy-mm-dd');
v_end_month_begin_day:=substr(in_end_day,0,8)||'01';v_poor_month:=fun_month_num(in_begin_day,in_end_day);v_sql:='select t.zs_group_id';
i:=0;
while i<=v_poor_month loop
if i=0 then
v_sql:=v_sql||',count(case when t.zs_bull_start_time>=(to_date('''||in_begin_day||' 00:00:00'',''yyyy-mm-dd hh24:mi:ss'')) and t.zs_bull_start_time<=(to_date('''||v_begin_month_last_day||' 23:59:59'',''yyyy-mm-dd hh24:mi:ss'')) then t.zs_bull_id end) "'||i||'"' ;
end if;
if i>0 and i<v_poor_month then
v_temp_first_day:=add_months(v_temp_first_day,1);
v_month_day:=to_char(v_temp_first_day,'yyyy-mm');
v_sql:=v_sql||',count(case when to_char(t.zs_bull_start_time,''yyyy-mm'')='''||v_month_day||''' then t.zs_bull_id end) "'||i||'"' ;
end if;
if i=v_poor_month then
v_sql:=v_sql||',count(case when t.zs_bull_start_time>=(to_date('''||v_end_month_begin_day||' 00:00:00'',''yyyy-mm-dd hh24:mi:ss'')) and t.zs_bull_start_time<=(to_date('''||in_end_day||' 23:59:59'',''yyyy-mm-dd hh24:mi:ss'')) then t.zs_bull_id end) "'||i||'"' ;
end if;
i:=i+1;
end loop;
v_sql:=v_sql||' from p_wanhua_zs_code t group by t.zs_group_id ';
DBMS_OUTPUT.PUT_LINE(v_sql);
open v_cur for v_sql;
end;
zs_group_id,月份,count(zs_bull_id)
创建一个临时表
CREATE global TEMPORARY TABLE test1
(id NUMBER(10),
month varchar(8),
seq number(10),
mnt number(10));
在过程里执行动态sql ;
insert into test1
select zs_group_id,to_char(zs_bull_start_time,'yyyymm'),months_between(trunc(t.month,'mm'),to_date(入参开始时间,'yyyymm')),count(*)
FROM p_wanhua_zs_code
GROUP BY zs_group_id,to_char(zs_bull_start_time,'yyyymm');
然后拼结果语句
SELECT ID,SUM(deocde(seq,0,mnt,0))......
FROM test1
GROUP BY ID;
OPEN v_cur for v_sql;
SQL>
SQL> CREATE OR REPLACE PROCEDURE proc_ttt_1
2 AS
3 v_sql LONG;
4 BEGIN
5 v_sql:='select 1 from dual';
6 EXECUTE IMMEDIATE v_sql;
7 END;
8 /
Procedure created
SQL> exec proc_ttt_1;
PL/SQL procedure successfully completed
SQL>
1、用BETWEEN代替 <= AND >= ,应该可以节省一定的长度
因为BETWEEN YYYYMMDD1 AND YYYYMMDD2是指 >=1的0时0分0秒,<= 2的0时0分0秒
也可以改为: >= YYYYMMDD1 AND < YYYYMMDD2 + 1,去掉时间部分
2、日期直接用 YYYYMMDDHH24MISS ,把那些连接符号去掉,应该也可以省一些这个只是在长度超出不是特别多的情况下看看
具体用法打开dbms_sql包就可以看到
v_select_clause varchar2(4000);
v_where_clause varchar2(4000); --- 一个不够的话,可以多定义几个,每个存储一部分的where子句
v_orderby_clause varchar2(4000);
然后就往里面塞SQL语句的各个部分
最后要打开游标时,就可以这样:open your_cursor for v_select_clause || v_where_clause || v_orderby_clause;
这个不能那样单纯的进行SQL 拼接 还是用Oracle的高级查询比较好