从存储的函数返回更新的行
Posted
技术标签:
【中文标题】从存储的函数返回更新的行【英文标题】:Return updated rows from a stored function 【发布时间】:2019-11-26 11:23:01 【问题描述】:我试图从 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)。
谢谢。
【问题讨论】:
@APC 不,我想要所有更新行的列表。 @APC 这是我实际功能的简化,我想更新具有特定状态的行并锁定它们,因为我有大量数据并且所有数据都必须发生在同一个数据库连接中。这就是为什么我需要一个带有 select * 的存储函数进行更新,然后更新选定的值。然后将所有选定的值返回给我的程序来处理它们。有点复杂…… 【参考方案1】:我将做一些假设:
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;
解决方案的要点。
使用 Oracle 维护的集合(数量)sys.odcinumberlist
来存储更新的 ID;
使用 RETURNING 子句捕获更新行的 id_table1
值;
将返回的密钥存储在集合中;
使用table()
函数将集合转换为可在引用游标中查询的表。
最后一点是我选择使用sys.odcinumberlist
而不是在过程中定义集合的原因。它是一种 SQL 类型,因此我们可以在 SELECT 语句中使用它。
(*) 逐行痛苦。在 PL/SQL 循环中更新单个记录是执行批量更新的最慢方式,通常构成反模式。一个简单的基于集合的 UPDATE 就足够了。但是,您知道自己的情况,所以我将保持原样。
【讨论】:
感谢您的建议!它现在正在工作。我知道这不是一般情况下的最佳实践,但是我们这里有一个复杂的应用程序,我们需要处理很多特定的细节,这不是正常情况。无论如何,你在这里拯救了一天:)【参考方案2】:在我看来,您不需要初始游标,因为您将不是“OK”的每一行的 STATUS 更改为“OK”,所以您可以通过一个简单的 UPDATE 语句来执行此操作。然后使用OPEN...FOR
语句返回STATUS 不是'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;
【讨论】:
【参考方案3】:而不是循环更新如何批量更新收集更新的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 ;
【讨论】:
以上是关于从存储的函数返回更新的行的主要内容,如果未能解决你的问题,请参考以下文章
用于插入/更新 MySql DB 的函数返回错误的“受影响的行”数
批量更新从更新返回了意外的行数(Spring/Hibernate)
TABLE 函数返回的行的顺序是不是保证与 VARRAY 元素的存储顺序相同?
如何解决“批量更新从更新返回意外的行数;实际行数:0;预期:1”问题?
StaleStateException:批量更新从更新 [0] 返回了意外的行数;实际行数:0;预期:1
HIbernate“StaleStateException:批量更新从更新 [0] 返回了意外的行数;实际行数:0;预期:1”