我可以通过名称从过程中访问游标吗?
Can I access a cursor from within a procedure by name?
游标名称将作为 varchar2 传入,并且游标本身与过程存在于同一包中。
仅给定名称(而不是游标引用)是否可以访问游标并循环遍历它?
如果这需要使用 "execute immediate" 或类似的东西,那也不是不可能的......(虽然我不太清楚它是如何工作的,但我对它声明的任何内容印象深刻完成后超出范围)。
提前致歉,这对我来说应该是显而易见的,但我一头雾水。
稍微考虑一下,我认为你的做法是错误的。我将每个 "cursors" 的所有结果 UNION 在一起,然后使用 "cursor name" 消除所有不需要的行(优化器应该优化掉这些行),这样你就只能得到你想要的行。所以像
CREATE OR REPLACE PROCEDURE DO_SOMETHING(pin_Cursor_name IN VARCHAR2)
IS
CURSOR csrFruits IS
SELECT UPPER(pin_Cursor_name) AS FRUIT_TYPE,
VARIETY_NAME,
COLOR,
SIZE,
TARTNESS_RATING,
NULL AS FUZZ_LENGTH,
ROOTSTOCK,
NULL AS PEEL_THICKNESS
FROM APPLES
WHERE pin_Cursor_name = 'apples'
UNION ALL
SELECT UPPER(pin_Cursor_name) AS FRUIT_TYPE,
VARIETY_NAME,
COLOR,
SIZE,
NULL AS TARTNESS_RATING,
FUZZ_LENGTH,
NULL AS ROOTSTOCK,
NULL AS PEEL_THICKNESS
FROM PEACHES
WHERE pin_Cursor_name = 'peaches'
UNION ALL
SELECT UPPER(pin_Cursor_name) AS FRUIT_TYPE,
VARIETY_NAME,
COLOR,
SIZE,
NULL AS TARTNESS_RATING,
NULL AS FUZZ_LENGTH,
NULL AS ROOTSTOCK,
PEEL_THICKNESS
FROM KUMQUATS
WHERE pin_Cursor_name = 'kumquats'
UNION ALL
SELECT UPPER(pin_Cursor_name) AS FRUIT_TYPE,
VARIETY_NAME,
'GREEN' AS COLOR,
SIZE,
NULL AS TARTNESS_RATING,
FUZZ_LENGTH,
ROOTSTOCK,
NULL AS PEEL_THICKNESS
FROM KIWIS
WHERE pin_Cursor_name = 'kiwis';
BEGIN
FOR aRow IN csrFruits LOOP
DBMS_OUTPUT.PUT_LINE(pin_Cursor_name || ' - ' ||
aRow.VARIETY_NAME || ', ' ||
aRow.COLOR || ', ' ||
aRow.SIZE);
END LOOP;
END DO_SOMETHING;
所以这里我们有一个游标,它将根据输入参数从四个不同的表(APPLES、PEACHES、KUMQUATS 和 KIWIS)中的一个读取。这个想法是让每个子查询 return 具有相同 "shape" 的行集,为每个子查询不提供的每一列添加 NULL AS XXX
。
祝你好运。
你原来的问题陈述有点含糊,我不清楚你有什么限制以及 "other system" 期望什么作为 return 值。你也可能有一个 XY-problem,所以@bobjarvis 的回答可能也有一个有效的观点。
这里的关键问题是在PL/SQL中无法转换explicit cursor to a cursor variable。因此,以下 "simple" 解决方案是不可能的:
-- pseudo PL/SQL code
cursor cur_a ...
cursor cur_b ...
function get_cursor(p_cur_name varchar2) return sys_refcursor is
v_cur sys_refcursor;
begin
execute immediate 'open v_cur for p_cur_name';
return v_cur;
end;
v_cur := get_cursor('cur_b');
在下面的示例包中,我假设所有游标都具有相同的结果集结构。我很懒惰,使用了弱游标变量,即使我应该使用强游标变量。包代码应该很容易理解。
至少还有一种可能对您有用的变体 - 将数据批量收集到一个集合中,然后使用其他子例程处理该集合。下面 print(varchar2)
只是演示如何使用 dbms_output.put_line
.
打开-迭代-关闭游标 "dynamically"
create or replace package so48 is
cursor cur_a is
select 'A1' as val, 1 as id from dual union all
select 'A2' as val, 2 as id from dual union all
select 'A3' as val, 3 as id from dual
;
cursor cur_b is
select 'B1' as val, 4 as id from dual union all
select 'B2' as val, 5 as id from dual union all
select 'B3' as val, 6 as id from dual
;
function get_cursor(p_cur_name in varchar2) return sys_refcursor;
procedure print(p_cur in sys_refcursor);
procedure print(p_cur_name in varchar2);
end;
/
show errors
create or replace package body so48 is
function get_cursor(p_cur_name in varchar2) return sys_refcursor is
v_cur sys_refcursor;
begin
case
when p_cur_name = 'A' then
open v_cur for
select 'A1' as val, 1 as id from dual union all
select 'A2' as val, 2 as id from dual union all
select 'A3' as val, 3 as id from dual
;
when p_cur_name = 'B' then
open v_cur for
select 'B1' as val, 4 as id from dual union all
select 'B2' as val, 5 as id from dual union all
select 'B3' as val, 6 as id from dual
;
else
null;
end case;
return v_cur;
end;
procedure print(p_cur in sys_refcursor) is
v_val varchar2(32767);
v_id number;
begin
loop
fetch p_cur into v_val, v_id;
exit when p_cur%notfound;
dbms_output.put_line('(val = ' || v_val || ')(id = ' || v_id || ')');
end loop;
end;
procedure print(p_cur_name in varchar2) is
plsql_compilation_error exception;
pragma exception_init(plsql_compilation_error, -6550);
v_cur_name constant varchar2(32767) := 'so48.' || p_cur_name;
v_plsql constant varchar2(32767) :=
q'[declare
v_val varchar2(32767);
v_id number;
begin
open ]' || v_cur_name || q'[;
loop
fetch ]' || v_cur_name || q'[ into v_val, v_id;
exit when ]' || v_cur_name || q'[%notfound;
dbms_output.put_line('(val = ' || v_val || ')(id = ' || v_id || ')');
end loop;
close ]' || v_cur_name || q'[;
end;]';
begin
execute immediate v_plsql;
exception
when plsql_compilation_error then
dbms_output.put_line('PL/SQL compilation error');
end;
end;
/
show errors
例子运行
SQL> exec so48.print(so48.get_cursor('A'))
(val = A1)(id = 1)
(val = A2)(id = 2)
(val = A3)(id = 3)
PL/SQL procedure successfully completed.
SQL> exec so48.print('cur_b')
(val = B1)(id = 4)
(val = B2)(id = 5)
(val = B3)(id = 6)
PL/SQL procedure successfully completed.
SQL>
游标名称将作为 varchar2 传入,并且游标本身与过程存在于同一包中。
仅给定名称(而不是游标引用)是否可以访问游标并循环遍历它?
如果这需要使用 "execute immediate" 或类似的东西,那也不是不可能的......(虽然我不太清楚它是如何工作的,但我对它声明的任何内容印象深刻完成后超出范围)。
提前致歉,这对我来说应该是显而易见的,但我一头雾水。
稍微考虑一下,我认为你的做法是错误的。我将每个 "cursors" 的所有结果 UNION 在一起,然后使用 "cursor name" 消除所有不需要的行(优化器应该优化掉这些行),这样你就只能得到你想要的行。所以像
CREATE OR REPLACE PROCEDURE DO_SOMETHING(pin_Cursor_name IN VARCHAR2)
IS
CURSOR csrFruits IS
SELECT UPPER(pin_Cursor_name) AS FRUIT_TYPE,
VARIETY_NAME,
COLOR,
SIZE,
TARTNESS_RATING,
NULL AS FUZZ_LENGTH,
ROOTSTOCK,
NULL AS PEEL_THICKNESS
FROM APPLES
WHERE pin_Cursor_name = 'apples'
UNION ALL
SELECT UPPER(pin_Cursor_name) AS FRUIT_TYPE,
VARIETY_NAME,
COLOR,
SIZE,
NULL AS TARTNESS_RATING,
FUZZ_LENGTH,
NULL AS ROOTSTOCK,
NULL AS PEEL_THICKNESS
FROM PEACHES
WHERE pin_Cursor_name = 'peaches'
UNION ALL
SELECT UPPER(pin_Cursor_name) AS FRUIT_TYPE,
VARIETY_NAME,
COLOR,
SIZE,
NULL AS TARTNESS_RATING,
NULL AS FUZZ_LENGTH,
NULL AS ROOTSTOCK,
PEEL_THICKNESS
FROM KUMQUATS
WHERE pin_Cursor_name = 'kumquats'
UNION ALL
SELECT UPPER(pin_Cursor_name) AS FRUIT_TYPE,
VARIETY_NAME,
'GREEN' AS COLOR,
SIZE,
NULL AS TARTNESS_RATING,
FUZZ_LENGTH,
ROOTSTOCK,
NULL AS PEEL_THICKNESS
FROM KIWIS
WHERE pin_Cursor_name = 'kiwis';
BEGIN
FOR aRow IN csrFruits LOOP
DBMS_OUTPUT.PUT_LINE(pin_Cursor_name || ' - ' ||
aRow.VARIETY_NAME || ', ' ||
aRow.COLOR || ', ' ||
aRow.SIZE);
END LOOP;
END DO_SOMETHING;
所以这里我们有一个游标,它将根据输入参数从四个不同的表(APPLES、PEACHES、KUMQUATS 和 KIWIS)中的一个读取。这个想法是让每个子查询 return 具有相同 "shape" 的行集,为每个子查询不提供的每一列添加 NULL AS XXX
。
祝你好运。
你原来的问题陈述有点含糊,我不清楚你有什么限制以及 "other system" 期望什么作为 return 值。你也可能有一个 XY-problem,所以@bobjarvis 的回答可能也有一个有效的观点。
这里的关键问题是在PL/SQL中无法转换explicit cursor to a cursor variable。因此,以下 "simple" 解决方案是不可能的:
-- pseudo PL/SQL code
cursor cur_a ...
cursor cur_b ...
function get_cursor(p_cur_name varchar2) return sys_refcursor is
v_cur sys_refcursor;
begin
execute immediate 'open v_cur for p_cur_name';
return v_cur;
end;
v_cur := get_cursor('cur_b');
在下面的示例包中,我假设所有游标都具有相同的结果集结构。我很懒惰,使用了弱游标变量,即使我应该使用强游标变量。包代码应该很容易理解。
至少还有一种可能对您有用的变体 - 将数据批量收集到一个集合中,然后使用其他子例程处理该集合。下面 print(varchar2)
只是演示如何使用 dbms_output.put_line
.
create or replace package so48 is
cursor cur_a is
select 'A1' as val, 1 as id from dual union all
select 'A2' as val, 2 as id from dual union all
select 'A3' as val, 3 as id from dual
;
cursor cur_b is
select 'B1' as val, 4 as id from dual union all
select 'B2' as val, 5 as id from dual union all
select 'B3' as val, 6 as id from dual
;
function get_cursor(p_cur_name in varchar2) return sys_refcursor;
procedure print(p_cur in sys_refcursor);
procedure print(p_cur_name in varchar2);
end;
/
show errors
create or replace package body so48 is
function get_cursor(p_cur_name in varchar2) return sys_refcursor is
v_cur sys_refcursor;
begin
case
when p_cur_name = 'A' then
open v_cur for
select 'A1' as val, 1 as id from dual union all
select 'A2' as val, 2 as id from dual union all
select 'A3' as val, 3 as id from dual
;
when p_cur_name = 'B' then
open v_cur for
select 'B1' as val, 4 as id from dual union all
select 'B2' as val, 5 as id from dual union all
select 'B3' as val, 6 as id from dual
;
else
null;
end case;
return v_cur;
end;
procedure print(p_cur in sys_refcursor) is
v_val varchar2(32767);
v_id number;
begin
loop
fetch p_cur into v_val, v_id;
exit when p_cur%notfound;
dbms_output.put_line('(val = ' || v_val || ')(id = ' || v_id || ')');
end loop;
end;
procedure print(p_cur_name in varchar2) is
plsql_compilation_error exception;
pragma exception_init(plsql_compilation_error, -6550);
v_cur_name constant varchar2(32767) := 'so48.' || p_cur_name;
v_plsql constant varchar2(32767) :=
q'[declare
v_val varchar2(32767);
v_id number;
begin
open ]' || v_cur_name || q'[;
loop
fetch ]' || v_cur_name || q'[ into v_val, v_id;
exit when ]' || v_cur_name || q'[%notfound;
dbms_output.put_line('(val = ' || v_val || ')(id = ' || v_id || ')');
end loop;
close ]' || v_cur_name || q'[;
end;]';
begin
execute immediate v_plsql;
exception
when plsql_compilation_error then
dbms_output.put_line('PL/SQL compilation error');
end;
end;
/
show errors
例子运行
SQL> exec so48.print(so48.get_cursor('A'))
(val = A1)(id = 1)
(val = A2)(id = 2)
(val = A3)(id = 3)
PL/SQL procedure successfully completed.
SQL> exec so48.print('cur_b')
(val = B1)(id = 4)
(val = B2)(id = 5)
(val = B3)(id = 6)
PL/SQL procedure successfully completed.
SQL>