Oracle 游标设计与重构问题
我有许多游标,它们都返回具有相同字段的行:一个数字ID字段和一个XMLType字段。每次访问这些游标中的一个(每个游标现在都有自己的访问函数),我都会经历相同的模式:Oracle 游标设计与重构问题,oracle,refactoring,plsql,cursor,Oracle,Refactoring,Plsql,Cursor,我有许多游标,它们都返回具有相同字段的行:一个数字ID字段和一个XMLType字段。每次访问这些游标中的一个(每个游标现在都有自己的访问函数),我都会经历相同的模式: --query behind cursor is designed to no more than one row. for rec in c_someCursor(in_searchKey => local_search_key_value) loop v_id := rec.ID v_someXMLVar
--query behind cursor is designed to no more than one row.
for rec in c_someCursor(in_searchKey => local_search_key_value) loop
v_id := rec.ID
v_someXMLVar := rec.XMLDataField
end loop;
if v_someXMLVar is null then
/* A bunch of mostly-standard error handling and logging goes here */
end if;
exception
/* all cursor access functions have the same error-handling */
end;
随着模式变得越来越明显,将其集中在单个功能中是有意义的:
function fn_standardCursorAccess(in_cursor in t_xmlCursorType, in_alt in XMLType) return XMLType is
v_XMLData XMLType;
begin
dbms_application_info.set_module(module_name => $$PLSQL_UNIT, action_name => 'fn_standardCursorAccess');
loop
fetch in_cursor
into v_XMLData;
exit when in_cursor%notfound;
end loop;
/*some additional standard processing goes here*/
return v_XML;
exception
/*standard exception handling happens here*/
end;
我遇到的问题是调用这个函数。我现在不得不这样称呼它:
open v_curs for select /*blah blah blah*/ where key_field = x and /*...*/;
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;
open v_curs for c_getSomeData(x);
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;
我想这样称呼它:
open v_curs for select /*blah blah blah*/ where key_field = x and /*...*/;
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;
open v_curs for c_getSomeData(x);
v_data := fn_standardCursorAccess(v_curs,alt);
close v_curs;
…原因是尽量减少对代码的更改量(我不想将所有这些游标剪切/粘贴到依赖它们的函数,如果多个函数依赖于同一个游标,我必须将其包装到新函数中)
不幸的是,这不起作用,Oracle返回一个错误
错误:PLS-00222:此范围内不存在名为“C_GETSOMEDATA”的函数
我想做的事可能吗
(Oracle版本为10.2)
编辑:
我认为描述我所做的更好的方法是将对显式游标的引用传递给一个函数,该函数将对游标返回的数据执行一些公共例程。
似乎我无法将open for语句与explicit游标一起使用,是否有其他方法可以获取对显式游标的引用,以便将该引用传递给函数?也许有别的方法可以解决这个问题
编辑:
复制和粘贴我先前对R Van Rijn回复的回复:
我尝试在包规范中声明游标,并使用包名引用它:open v_curs for PKG.c_getSomeData(x);。。。这给了我一个新的错误,即PKG.c_getSomeData必须是一个函数或数组才能以这种方式使用
更新:
我在这里和我们的DBA谈过,他说ref游标不可能指向显式游标。看来我毕竟不能这么做。糟糕透了(关于错误PLS-00222: 作为函数“c_getSomeData”引用的标识符未声明或实际上表示另一个对象(例如,它可能已声明为过程) 检查标识符的拼写和声明。同时确认声明正确放置在块结构中
这意味着您必须创建一个实际返回某些值的函数。关于错误PLS-00222: 作为函数“c_getSomeData”引用的标识符未声明或实际上表示另一个对象(例如,它可能已声明为过程) 检查标识符的拼写和声明。同时确认声明正确放置在块结构中
这意味着您必须创建一个实际返回某些值的函数。此测试脚本和输出是否表示您正在尝试执行的操作?我将光标变量=设置为函数的输出,而不是
为c_getSomeData(x)打开v_curs;
我们的测试数据:
set serveroutput on
--create demo table
drop table company;
create table company
(
id number not null,
name varchar2(40)
);
insert into company (id, name) values (1, 'Test 1 Company');
insert into company (id, name) values (2, 'Test 2 Company');
insert into company (id, name) values (3, 'Test 3 Company');
commit;
创建包
create or replace package test_pkg as
type cursor_type is ref cursor;
function c_getSomeData(v_companyID number) return cursor_type;
end test_pkg;
/
create or replace package body test_pkg as
function c_getSomeData(v_companyID number) return cursor_type
is
v_cursor cursor_type;
begin
open v_cursor for
select id,
name
from company
where id = v_companyID;
return v_cursor;
end c_getSomeData;
end test_pkg;
/
运行我们的程序
declare
c test_pkg.cursor_type;
v_id company.id%type;
v_name company.name%type;
begin
c := test_pkg.c_getSomeData(1);
loop
fetch c
into v_id, v_name;
exit when c%notfound;
dbms_output.put_line(v_id || ' | ' || v_name);
end loop;
close c;
end;
/
1 | Test 1 Company
PL/SQL procedure successfully completed.
此测试脚本和输出是否表示您正在尝试执行的操作?我将光标变量=设置为函数的输出,而不是
openv_curs for c_getSomeData(x);
我们的测试数据:
set serveroutput on
--create demo table
drop table company;
create table company
(
id number not null,
name varchar2(40)
);
insert into company (id, name) values (1, 'Test 1 Company');
insert into company (id, name) values (2, 'Test 2 Company');
insert into company (id, name) values (3, 'Test 3 Company');
commit;
创建包
create or replace package test_pkg as
type cursor_type is ref cursor;
function c_getSomeData(v_companyID number) return cursor_type;
end test_pkg;
/
create or replace package body test_pkg as
function c_getSomeData(v_companyID number) return cursor_type
is
v_cursor cursor_type;
begin
open v_cursor for
select id,
name
from company
where id = v_companyID;
return v_cursor;
end c_getSomeData;
end test_pkg;
/
运行我们的程序
declare
c test_pkg.cursor_type;
v_id company.id%type;
v_name company.name%type;
begin
c := test_pkg.c_getSomeData(1);
loop
fetch c
into v_id, v_name;
exit when c%notfound;
dbms_output.put_line(v_id || ' | ' || v_name);
end loop;
close c;
end;
/
1 | Test 1 Company
PL/SQL procedure successfully completed.
我承认我发现你的需求有点难以预测。你已经发布了很多代码,但正如我在评论中所建议的,并不是说明问题的部分。因此,下面的内容可能有点离谱。但这是一个有趣的问题 下面的代码显示了我们如何定义一个通用的geneneric REF游标,使用来自不同查询的特定数据填充它,然后以标准化的方式处理它们。再次,如果这不符合您的业务逻辑,我深表歉意;如果是这种情况,请编辑您的问题以解释我在哪里制作了bloomer 这是通用的ref游标
create or replace package type_def is
type xml_rec is record (id number, payload xmltype);
type xml_cur is ref cursor return xml_rec;
end type_def;
/
这是标准处理器
create or replace procedure print_xml_cur
( p_cur in type_def.xml_cur )
is
lrec type_def.xml_rec;
begin
loop
fetch p_cur into lrec;
exit when p_cur%notfound;
dbms_output.put_line('ID='||lrec.id);
dbms_output.put_line('xml='||lrec.payload.getClobVal());
end loop;
close p_cur;
end print_xml_cur;
/
返回带有不同数据的标准光标的两个过程
create or replace function get_emp_xml
( p_id in emp.deptno%type )
return type_def.xml_cur
is
return_value type_def.xml_cur;
begin
open return_value for
select deptno
, sys_xmlagg(sys_xmlgen(ename))
from emp
where deptno = p_id
group by deptno;
return return_value;
end get_emp_xml;
/
create or replace function get_dept_xml
( p_id in dept.deptno%type )
return type_def.xml_cur
is
return_value type_def.xml_cur;
begin
open return_value for
select deptno
, sys_xmlagg(sys_xmlgen(dname))
from dept
where deptno = p_id
group by deptno;
return return_value;
end get_dept_xml;
/
现在让我们把这一切放在一起
SQL> set serveroutput on size unlimited
SQL>
SQL> exec print_xml_cur(get_emp_xml(40))
ID=40
xml=<?xml
version="1.0"?>
<ROWSET>
<ENAME>GADGET</ENAME>
<ENAME>KISHORE</ENAME>
</ROWSET>
PL/SQL procedure successfully completed.
SQL> exec print_xml_cur(get_dept_xml(20))
ID=20
xml=<?xml version="1.0"?>
<ROWSET>
<DNAME>RESEARCH</DNAME>
</ROWSET>
PL/SQL procedure successfully completed.
SQL>
SQL>将serveroutput设置为大小不限
SQL>
SQL>exec打印xml(获取emp xml(40))
ID=40
xml=
小工具
基肖尔
PL/SQL过程已成功完成。
SQL>exec打印xml(获取部门xml(20))
ID=20
xml=
研究
PL/SQL过程已成功完成。
SQL>
我承认,我发现您的需求有点难以预测。您发布了很多代码,但正如我在评论中所建议的,并不是说明问题的部分。因此,以下内容可能有点离谱。但这是一个有趣的问题
下面的代码显示了我们如何定义一个通用的geneneric REF游标,使用来自不同查询的特定数据填充它,然后以标准化的方式处理它们。再次,如果这不符合您的业务逻辑,我深表歉意;如果是这种情况,请编辑您的问题以解释我在哪里制作了bloomer
这是通用的ref游标
create or replace package type_def is
type xml_rec is record (id number, payload xmltype);
type xml_cur is ref cursor return xml_rec;
end type_def;
/
这是标准处理器
create or replace procedure print_xml_cur
( p_cur in type_def.xml_cur )
is
lrec type_def.xml_rec;
begin
loop
fetch p_cur into lrec;
exit when p_cur%notfound;
dbms_output.put_line('ID='||lrec.id);
dbms_output.put_line('xml='||lrec.payload.getClobVal());
end loop;
close p_cur;
end print_xml_cur;
/
返回带有不同数据的标准光标的两个过程
create or replace function get_emp_xml
( p_id in emp.deptno%type )
return type_def.xml_cur
is
return_value type_def.xml_cur;
begin
open return_value for
select deptno
, sys_xmlagg(sys_xmlgen(ename))
from emp
where deptno = p_id
group by deptno;
return return_value;
end get_emp_xml;
/
create or replace function get_dept_xml
( p_id in dept.deptno%type )
return type_def.xml_cur
is
return_value type_def.xml_cur;
begin
open return_value for
select deptno
, sys_xmlagg(sys_xmlgen(dname))
from dept
where deptno = p_id
group by deptno;
return return_value;
end get_dept_xml;
/
现在让我们把这一切放在一起
SQL> set serveroutput on size unlimited
SQL>
SQL> exec print_xml_cur(get_emp_xml(40))
ID=40
xml=<?xml
version="1.0"?>
<ROWSET>
<ENAME>GADGET</ENAME>
<ENAME>KISHORE</ENAME>
</ROWSET>
PL/SQL procedure successfully completed.
SQL> exec print_xml_cur(get_dept_xml(20))
ID=20
xml=<?xml version="1.0"?>
<ROWSET>
<DNAME>RESEARCH</DNAME>
</ROWSET>
PL/SQL procedure successfully completed.
SQL>
SQL>将serveroutput设置为大小不限
SQL>
SQL>exec打印xml(获取emp xml(40))
ID=40
xml=
小工具
基肖尔
PL/SQL过程已成功完成。
SQL>exec打印xml(获取部门xml(20))
ID=20
xml=
研究
PL/SQL过程已成功完成。
SQL>
看来我想做的事情(让一个语句引用打开一个现有的显式游标)在Oracle中是不允许的。(看来我想做的事情(让一个语句引用打开一个现有的显式游标)在Oracle中是不允许的。:(好的,Oracle的简短回答是:“做不到!”