【问题标题】:Can I access a cursor from within a procedure by name?我可以按名称从过程中访问游标吗?
【发布时间】:2015-07-10 14:29:55
【问题描述】:

游标名称将作为 varchar2 传入,并且游标本身与过程存在于同一包中。

只给定名称(而不是游标引用)是否可以访问游标并循环遍历它?

如果这需要使用“立即执行”或类似的,那不是不可能的......(虽然我不太清楚它是如何工作的,但我的印象是它声明的任何内容都超出了范围一旦完成)。

提前道歉,这对我来说似乎应该是显而易见的,但我是空白的。

【问题讨论】:

  • 通过引用传递光标会简化您的问题
  • 该过程将直接运行,不会被另一个过程调用......并且来自另一个只能传入字符串的系统。
  • “其他系统”可以处理引用游标吗?如果是这样,您可以只获取传入的字符串并为此打开引用光标,例如open cur_var for p_sql_string,然后将 ref 光标传回另一个系统,让它循环。
  • 如何将光标作为 varchar2 存储在具有绑定变量的表中?然后传递光标的 ID 并在过程中打开它
  • 我的运气为零,立即执行'open'||光标名称 || '(参数 1,参数 2)'; ... 一直被 ORA-00900 无效的 SQL 语句阻塞。我不相信 open 是立即执行的有效语句。并且 open-for 只将语句作为字符串,而不是显式命名的游标。

标签: oracle plsql cursor


【解决方案1】:

稍微考虑一下,我认为您的做法是错误的。我会将来自每个“游标”的所有结果合并在一起,然后使用“游标名称”来消除所有不需要的行(优化器应该优化掉这些行),以便您只获得所需的行。所以像

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)之一中读取。这个想法是让每个子查询返回一个相同“形状”的行集,为单个子查询不提供的每一列添加NULL AS XXX

祝你好运。

【讨论】:

  • 这种方法有一些优点。我正在处理一些可能使这变得困难的约束。我认为其他人都不敢回答,对于更一般的情况,这实际上是一个很好的答案……所以我接受了。谢谢你的好运,我需要它。
【解决方案2】:

您最初的问题陈述相当模糊,我不清楚您有什么限制以及“其他系统”期望作为返回值的内容。你也可能有XY-problem,所以@bobjarvis 的回答也可能有道理。

这里的关键问题是在 PL/SQL 中无法将 explicit cursor 转换为 cursor variable。因此,以下“简单”解决方案是不可能的:

-- 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>

【讨论】:

  • 我忘了明确提到显式游标必须在包的公共接口中,以便execute immediate中的SQL引擎可以看到游标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-06
  • 1970-01-01
  • 2012-09-06
  • 2013-07-14
  • 2011-05-14
  • 2011-06-25
  • 1970-01-01
相关资源
最近更新 更多