Oracle修改sys refcursor并以PL/SQL返回修改后的游标

Oracle修改sys refcursor并以PL/SQL返回修改后的游标,oracle,stored-procedures,plsql,oracle18c,plsql-package,Oracle,Stored Procedures,Plsql,Oracle18c,Plsql Package,我正在尝试创建一个过程,该过程将sys refcursor作为in out参数,并根据下面代码注释中解释的逻辑对其进行修改 TYPE t_params IS TABLE OF VARCHAR2(32767 CHAR); / CREATE OR REPLACE PROCEDURE modify_cursor ( p_cursor IN OUT SYS_REFCURSOR, p_array_binary IN t_params, p_values IN t_params

我正在尝试创建一个过程,该过程将sys refcursor作为in out参数,并根据下面代码注释中解释的逻辑对其进行修改

TYPE t_params IS
  TABLE OF VARCHAR2(32767 CHAR);
  /
CREATE OR REPLACE PROCEDURE modify_cursor (
p_cursor IN OUT SYS_REFCURSOR,
p_array_binary   IN t_params,
p_values         IN t_params
)
/*

p_cursor IN OUT SYS_REFCURSOR
-- contains a single row {empId:123, ename:"king", mgr:"Porter",deptNo:200}
p_array_binary  IN t_params
-- contains one binary value corresponding to each column in above cursor ["1","0","1","1"] 
p_values  IN t_params
-- contains one binary value corresponding to each column in above cursor ["123","king2","new manager","200"]



*/
IS

BEGIN
    /*
        Based on p_array_binary 
           if binary value 0 then take cursor should retain value as it is fro corresponding column
           if binary value 1 then cusrsor should have the correspondoing column value from p_values 


    In short, the out cursor should be      {empId:123, ename:"king", mgr:"new manager", deptNo:200}     

*/

END;
/

非常感谢您在这方面提供的任何帮助。

如果您知道ref cursor结构-它始终是所示数据类型的四列-那么这将相对简单:

CREATE OR REPLACE PROCEDURE modify_cursor (
p_cursor IN OUT SYS_REFCURSOR,
p_array_binary   IN t_params,
p_values         IN t_params
)
IS
  l_empid number;
  l_ename varchar2(30);
  l_mgr varchar2(30);
  l_deptNo number;
BEGIN
  -- get original values into local variables
  fetch p_cursor into l_empId, l_ename, l_mgr, l_deptNo;
  -- re-open cursor using either local variables of p_values depending on p_binary flag
  open p_cursor for
    select
      case when p_array_binary(1) = '1' then to_number(p_values(1)) else l_empId end as empId,
      case when p_array_binary(2) = '1' then p_values(2) else l_ename end as ename,
      case when p_array_binary(3) = '1' then p_values(3) else l_mgr end as mgr,
      case when p_array_binary(4) = '1' then to_number(p_values(4)) else l_deptNo end as deptNo
    from dual;
END;
/
通过SQL*Plus/SQL Developer/SQLcl绑定变量使用示例数据演示:

var rc refcursor;

begin
  open :rc for
    select 123 as empId, 'king' as ename, 'Porter' as mgr, 200 as deptNo
    from dual;

  modify_cursor(:rc, t_params('1', '0', '1', '1'), t_params('123', 'king2', 'new manager', '200'));
end;
/

print rc

     EMPID ENAME                            MGR                                  DEPTNO
---------- -------------------------------- -------------------------------- ----------
       123 king                             new manager                             200

由于您事先不知道结构,因此必须使用动态SQL,这有点复杂。这里有一个提纲:

CREATE OR REPLACE PROCEDURE modify_cursor (
p_cursor IN OUT SYS_REFCURSOR,
p_array_binary   IN t_params,
p_values         IN t_params
)
IS
  l_c integer;
  l_col_cnt integer;
  l_desc_t dbms_sql.desc_tab3;
  l_varchar2 varchar2(32767 char);
  l_values t_params := new t_params();
  l_result integer;
BEGIN
  -- convert ref cursor to dbms_sql cursor
  l_c := dbms_sql.to_cursor_number(rc => p_cursor);
  -- analyse the cursor (columns, data types)
  dbms_sql.describe_columns3(c => l_c, col_cnt => l_col_cnt, desc_t => l_desc_t);
  -- optionally check l_col_cnt matches sise of t_params arguments?
  l_values.extend(l_col_cnt);

  -- define each column for fetch; here you're treating everything as strings,
  -- which will cause issues with some other data types
  for i in 1..l_col_cnt loop
    dbms_sql.define_column(c => l_c, position => i, column => l_varchar2, column_size => 32767);
  end loop;

  -- fetch original values - only one row to worry about so no loop
  l_result := dbms_sql.fetch_rows(c => l_c);

  for i in 1..l_col_cnt loop
    -- depending on p_array_binary, set l_values from either fetched data or p_values
    if p_array_binary(i) = '1' then
      l_values(i) := p_values(i);
    else
      -- this forces everything to varchar2, which is OK (ish) for your sample data;
      -- if you have other data types e.g. dates then you will probably want type-specific
      -- handling so you can control the conversions - which affects this, define_column
      -- and the final cursor to retrieve the values. But you have the same issue with p_values.
      dbms_sql.column_value(c => l_c, position => i, value => l_values(i));
    end if;
  end loop;

  -- finished with original cursor, so close it
  dbms_sql.close_cursor(c => l_c);

  -- re-open ref cursor using l_values data, with another dynamic SQL statement
  l_varchar2 := 'select ';
  for i in 1..l_col_cnt loop
    if i > 1 then
      l_varchar2 := l_varchar2 || ', ';
    end if;
    if l_desc_t(i).col_type = 2 then
      l_varchar2 := l_varchar2 || l_values(i);
    else
      l_varchar2 := l_varchar2 || '''' || l_values(i) || '''';
    end if;
    l_varchar2 := l_varchar2 || ' as "' || l_desc_t(i).col_name || '"';
  end loop;
  l_varchar2 := l_varchar2 || ' from dual';
  open p_cursor for l_varchar2;
END;
/
运行完全相同的演示程序块可提供:

     EMPID ENAM MGR             DEPTNO
---------- ---- ----------- ----------
       123 king new manager        200

如果需要,您可以添加其他数据类型的处理、错误处理等


了解更多信息。

您不能更改光标。你只能用相应的数据创建一个新的。也许我可以创建一个新的游标并将其重新分配给out参数p_cursor。但是如何实现呢?或者有没有办法将游标单行结果转换为数组呢?游标中的单行是一个包含四个属性或四列的JSON值?无论哪种方式,它总是四个-都在游标中,并且都在
t_params
arguments中有四个元素-或者它的四列会有所不同。它并不总是四个。它可以变化。t_参数中元素的数量取决于ref cursorNow中列的数量这太棒了。