Если бы вы знали структуру курсора ref - это всегда было четыре столбца с указанными типами данных - тогда это было бы относительно просто:
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
db <> fiddle
Поскольку вы не знаете структуру заранее, вам придется используйте Dynami c 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
db <> fiddle
Вы можете добавить обработку для других типов данных, если необходимо, обработка ошибок и т. д. c.
Подробнее о dbms_ sql.