【问题标题】:Return updated rows from a stored function从存储的函数返回更新的行
【发布时间】:2023-03-11 04:20: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 * 的存储函数进行更新,然后更新选定的值。然后将所有选定的值返回给我的程序来处理它们。有点复杂……

标签: oracle stored-procedures plsql stored-functions


【解决方案1】:

我将做一些假设:

  1. id_table1 是表的主键,因此您的 RBAR (*) 更新只影响一行
  2. 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 ;
    

    【讨论】:

      猜你喜欢
      • 2016-02-05
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-17
      • 2016-11-26
      • 2013-09-24
      • 2016-07-03
      相关资源
      最近更新 更多