|
|
补充些以前工作中遇到的实际例子:
数据库版本:10.2.0.4
create table zsj_objs as select * from dba_objects;
使用本地动态SQL实现:
create or replace function f_zsj_cursor(v_owner varchar2,v_object_type varchar2,v_object_id number,v_created date) return sys_refcursor
is
v_cursor sys_refcursor;
v_sql varchar2(32000);
type t_strlist is table of varchar2(100);
v_strlist t_strlist:=t_strlist();
v_nls_date_format NLS_SESSION_PARAMETERS.value%type;
begin
v_sql:='select * from zsj_objs where ';
if(v_owner is not null) then
v_strlist.extend();
v_strlist(v_strlist.count):=upper(v_owner);
v_sql:=v_sql||'owner=:'||v_strlist.count||' and ';
end if;
if(v_object_type is not null) then
v_strlist.extend();
v_strlist(v_strlist.count):=upper(v_object_type);
v_sql:=v_sql||'object_type=:'||v_strlist.count||' and ';
end if;
if(v_object_id is not null) then
v_strlist.extend();
v_strlist(v_strlist.count):=v_object_id;
v_sql:=v_sql||'object_id>=:'||v_strlist.count||' and ';
end if;
if(v_created is not null) then
v_strlist.extend();
v_strlist(v_strlist.count):=to_char(v_created,'yyyy-mm-dd hh24:mi:ss');
v_sql:=v_sql||'created>=:'||v_strlist.count;
end if;
v_sql:=regexp_replace(v_sql,' and $');
v_sql:=regexp_replace(v_sql,' where $');
SELECT VALUE into v_nls_date_format FROM NLS_SESSION_PARAMETERS WHERE PARAMETER='NLS_DATE_FORMAT';
execute immediate 'alter session set nls_date_format=''yyyy-mm-dd hh24:mi:ss''';
--下面这部分是静态的,而不是动态的,在最大绑定变量数量可知的情况下,代码冗长些,但至少还可以实现,但如果最大绑定变量数量都不确定的话,这里的代码实现不了
case v_strlist.count
when 0 then
open v_cursor for v_sql;
when 1 then
open v_cursor for v_sql using v_strlist(1);
when 2 then
open v_cursor for v_sql using v_strlist(1),v_strlist(2);
when 3 then
open v_cursor for v_sql using v_strlist(1),v_strlist(2),v_strlist(3);
when 4 then
open v_cursor for v_sql using v_strlist(1),v_strlist(2),v_strlist(3),v_strlist(4);
end case;
execute immediate 'alter session set nls_date_format='''||v_nls_date_format||'''';
/*不是太明白nls_变量是open cursor时起作用还是fetch cursor时起作用,如果是fetch cursor时起作用的话,也只能是在调用这个函数的客户端关闭这个引用游标之后把这个nls_变量改回原来的设置值了*/
return v_cursor;
/*这里object_id,created列上的索引都是可供使用的,因为这些列上并不会应用任何表达式的
where object_id>='50000' 实际上执行的是where object_id>=to_number('50000')
where created>='2009-07-26 12:00:00' 实际上执行的是where created>=to_date(' 2009-07-26 12:00:00','syyyy-mm-dd hh24:mi:ss')
*/
end;
/
但其实很长时间以来一直不知道,如果绑定变量的最大数量都不确定的话,应该如何解决这个问题。
直到看这个帖子时,看到newkid版主的http://www.itpub.net/viewthread.php?tid=1019164
这个帖子才发现其实可以用dbms_sql实现.
那就贴一下dbms_sql的实现版本
使用dbms_sql实现的话,确实要灵活得多.但在11g前,dbms_sql好像还不支持sys_refcursor,所以如果要使用dbms_sql实现上面的逻辑的话,我现在只能使用一个临时表了,不知道是否还有好的实现方式.
CREATE GLOBAL temporary TABLE zsj_objs_temp ON COMMIT PRESERVE rows AS SELECT * FROM zsj_objs WHERE 1=0;
create or replace function f_zsj_cursor2(v_owner varchar2,v_object_type varchar2,v_object_id number,v_created date) return sys_refcursor
is
v_cursor sys_refcursor;
v_sql varchar2(32000);
type t_strlist is table of varchar2(100);
v_strlist t_strlist:=t_strlist();
v_nls_date_format NLS_SESSION_PARAMETERS.value%type;
l_theCursor INTEGER;
l_status INTEGER;
BEGIN
DELETE FROM zsj_objs_temp;
v_sql:='insert into zsj_objs_temp select * from zsj_objs where ';
if(v_owner is not null) then
v_strlist.extend();
v_strlist(v_strlist.count):=upper(v_owner);
v_sql:=v_sql||'owner=:'||v_strlist.count||' and ';
end if;
if(v_object_type is not null) then
v_strlist.extend();
v_strlist(v_strlist.count):=upper(v_object_type);
v_sql:=v_sql||'object_type=:'||v_strlist.count||' and ';
end if;
if(v_object_id is not null) then
v_strlist.extend();
v_strlist(v_strlist.count):=v_object_id;
v_sql:=v_sql||'object_id>=:'||v_strlist.count||' and ';
end if;
if(v_created is not null) then
v_strlist.extend();
v_strlist(v_strlist.count):=to_char(v_created,'yyyy-mm-dd hh24:mi:ss');
v_sql:=v_sql||'created>=:'||v_strlist.count;
end if;
v_sql:=regexp_replace(v_sql,' and $');
v_sql:=regexp_replace(v_sql,' where $');
SELECT VALUE into v_nls_date_format FROM NLS_SESSION_PARAMETERS WHERE PARAMETER='NLS_DATE_FORMAT';
execute immediate 'alter session set nls_date_format=''yyyy-mm-dd hh24:mi:ss''';
l_theCursor:=Dbms_Sql.OPEN_cursor;
Dbms_Sql.parse(c=>l_theCursor,STATEMENT=>v_sql,language_flag=>Dbms_Sql.native);
FOR i IN 1..v_strlist.Count
LOOP
Dbms_Sql.bind_variable(c=>l_theCursor,NAME=>':'||i,Value=>v_strlist(i));
END LOOP;
l_status:=Dbms_Sql.EXECUTE(l_theCursor);
COMMIT;
execute immediate 'alter session set nls_date_format='''||v_nls_date_format||''''; --alter session操作不会commit的
OPEN v_cursor FOR SELECT * FROM zsj_objs_temp;
return v_cursor;
end;
/
下面这个执行报错,因为顺序不对,应该是解析后才能绑定变量的.
CREATE OR REPLACE function f_zsj_cursor3(v_owner varchar2,v_object_type varchar2,v_object_id number,v_created date) return sys_refcursor
is
v_cursor sys_refcursor;
v_sql varchar2(32000);
l_theCursor INTEGER;
l_status INTEGER;
v_var_num INTEGER:=0;
BEGIN
DELETE FROM zsj_objs_temp;
v_sql:='insert into zsj_objs_temp select * from zsj_objs where ';
if(v_owner is not null) then
v_var_num:=v_var_num+1;
v_sql:=v_sql||'owner=:'||v_var_num||' and ';
Dbms_Sql.bind_variable(c=>l_theCursor,NAME=>':'||v_var_num,Value=>upper(v_owner));
end if;
if(v_object_type is not null) then
v_var_num:=v_var_num+1;
v_sql:=v_sql||'object_type=:'||v_var_num||' and ';
Dbms_Sql.bind_variable(c=>l_theCursor,NAME=>':'||v_var_num,Value=>upper(v_object_type));
end if;
if(v_object_id is not null) then
v_var_num:=v_var_num+1;
v_sql:=v_sql||'object_id>=:'||v_var_num||' and ';
Dbms_Sql.bind_variable(c=>l_theCursor,NAME=>':'||v_var_num,Value=>v_object_id);
end if;
if(v_created is not null) then
v_var_num:=v_var_num+1;
v_sql:=v_sql||'created>=:'||v_var_num;
Dbms_Sql.bind_variable(c=>l_theCursor,NAME=>':'||v_var_num,Value=>v_created);
end if;
v_sql:=regexp_replace(v_sql,' and $');
v_sql:=regexp_replace(v_sql,' where $');
l_theCursor:=Dbms_Sql.OPEN_cursor;
Dbms_Sql.parse(c=>l_theCursor,STATEMENT=>v_sql,language_flag=>Dbms_Sql.native);
l_status:=Dbms_Sql.EXECUTE(l_theCursor);
COMMIT;
OPEN v_cursor FOR SELECT * FROM zsj_objs_temp;
return v_cursor;
end;
/
[ 本帖最后由 zhaosj1726 于 2010-5-14 12:05 编辑 ] |
|