我正在尝试从ORACLE中的表中选择一些行,同时更新所选行的状态。我找到了一种使用存储的函数和游标执行此操作的方法,但是在使用游标进行更新后我无法设法返回行。这是我的代码:
CREATE OR REPLACE FUNCTION FUNCTION_NAME
RETURN SYS_REFCURSOR
IS
l_return SYS_REFCURSOR;
CURSOR c_operations IS
SELECT * FROM TABLE1
WHERE STATUS != 'OK'
FOR UPDATE OF TABLE1.STATUS;
BEGIN
FOR r_operation IN c_operations
LOOP
UPDATE
TABLE1
SET
TABLE1.STATUS = 'OK'
WHERE
TABLE1.ID_TABLE1 = r_operation.ID_TABLE1;
END LOOP;
COMMIT;
-- Missing conversion from cursor to sys_refcursor
RETURN l_return;
END;
该更新正在运行,但是我仍然缺少如何返回游标中更新后的行(c_operations)。
谢谢。
我要作一些假设:
id_table1
是表的主键,因此您的RBAR (*)更新仅影响一行id_table1
是数字如果这些假设是错误的,则需要调整以下代码。
CREATE OR REPLACE FUNCTION FUNCTION_NAME
RETURN SYS_REFCURSOR
IS
l_return SYS_REFCURSOR;
l_id table1.id_table1%type;
l_upd_ids sys.odcinumberlist := new sys.odcinumberlist();
CURSOR c_operations IS
SELECT * FROM TABLE1
WHERE STATUS != 'OK'
FOR UPDATE OF TABLE1.STATUS;
BEGIN
FOR r_operation IN c_operations LOOP
UPDATE TABLE1
SET TABLE1.STATUS = 'OK'
WHERE TABLE1.ID_TABLE1 = r_operation.ID_TABLE1
returning TABLE1.ID_TABLE1 into l_id;
l_upd_ids.extend();
l_upd_ids(l_upd_ids.count()) := l_id;
END LOOP;
COMMIT;
open l_return for
select * from table(l_upd_ids);
RETURN l_return;
END;
解决方案的关键点。
sys.odcinumberlist
集合来存储更新的ID;id_table1
值;table()
函数将集合简化为一个表,可以在引用游标中查询该表。最后一点是为什么我选择使用sys.odcinumberlist
而不是在过程中定义一个集合的原因。这是一种SQL类型,因此我们可以在SELECT语句中使用它。
(*)逐行进行。在PL / SQL循环中更新单个记录是执行批量更新的最慢方法,通常构成一种反模式。一个简单的基于集合的UPDATE就足够了。但是,您知道自己的处境,所以我将保持现状。
在我看来,您不需要初始光标,因为您正在将不是'OK'的每一行的STATUS更改为'OK',因此您可以执行此操作,这是一个简单的UPDATE语句。然后使用OPEN...FOR
语句返回状态不是'OK'
的所有行的游标,因为您已经将所有状态值更改为'OK'
,所以该游标不应该返回任何内容。我建议您将过程重写为:
CREATE OR REPLACE FUNCTION FUNCTION_NAME
RETURN SYS_REFCURSOR
IS
l_return SYS_REFCURSOR;
BEGIN
UPDATE TABLE1
SET STATUS = 'OK'
WHERE STATUS != 'OK';
COMMIT;
OPEN l_return FOR SELECT *
FROM TABLE1
WHERE STATUS != 'OK'
FOR UPDATE OF TABLE1.STATUS;
RETURN l_return;
END;
而不是循环来更新批量更新收集更新的ID的方式。然后从这些返回的id中创建表函数。
create type t_table1_id is
table of integer;
create or replace function set_table1_status_ok
return sys_refcursor
is
l_results_cursor sys_refcursor;
l_updated_ids t_table1_id;
begin
update table1
set status = 'Ok'
where status != 'Ok'
returning table1.id
bulk collect
into l_updated_ids;
open l_results_cursor for
select *
from table1
where id in (select * from table(l_updated_ids));
return l_results_cursor;
end set_table1_status_ok;
-- test
declare
updated_ids sys_refcursor;
l_this_rec table1%rowtype;
begin
updated_ids := set_table1_status_ok();
loop
fetch updated_ids into l_this_rec;
exit when updated_ids%notfound;
dbms_output.put_line ( l_this_rec.id || ' updated.');
end loop;
close updated_ids;
end ;