Oracle PL/SQL 在循环中捕获锁定异常并继续

Posted

技术标签:

【中文标题】Oracle PL/SQL 在循环中捕获锁定异常并继续【英文标题】:Oracle PL/SQL capture lock exception in loop and continue 【发布时间】:2016-11-28 02:26:01 【问题描述】:

我在下面有一个过程,它通过游标循环执行一些逻辑。我已将 FOR UPDATE NOWAIT 放在光标上以锁定我的记录集,以防其他会话中的某人想要更新同一集。

但是可能有这样一种情况,我在光标中选择的记录也被某人锁定了,在这种情况下,我想简单地将锁定的记录记录到日志表中,然后继续到下一条记录循环。

PROCEDURE test(p_id IN NUMBER)
IS

  CURSOR cur_test IS
  SELECT emp_id,
         emp_name
  FROM  
         EMP
  FOR UPDATE NOWAIT;

  row_locked EXCEPTION;
  PRAGMA EXCEPTION_INIT(row_locked, -54);  

BEGIN
  FOR r_cur_test  IN cur_test 
  LOOP
    BEGIN

      --do something

    EXCEPTION
      WHEN row_locked THEN
        --log locked record in log table
        log_lock('Record is locked');  
        COMMIT; 
    END;      
  END LOOP;

  --call log function to log a successful run

  COMMIT;
EXCEPTION
  WHEN OTHERS THEN  
    --Unsuccessful completion of the run.Trap all unhandled exceptions.
    --log error in error table    
    log_lock('Process exited with error');   
    ROLLBACK;

END;

为了测试,我打开会话 1 并执行

select * from EMP FOR UPDATE NOWAIT

在会话 2 中,我执行了

begin
  test(1);
end;

它总是进入 OTHERS 异常,因此,procecure 刚刚终止,没有继续循环游标。

谁能给我一些建议?非常感谢

【问题讨论】:

那么,当其他人阻塞时有什么异常?这是一团糟。 光标后面有更多代码for循环,而when others块中的异常就是那个。 那么,有什么例外???如果您无法提供所有相关信息,您希望有人如何帮助您? 您确定在尝试锁定记录时引发了异常。我建议将异常块一起删除一段时间,然后观察引发了什么异常。 @WilliamMu 如果您没有在异常块中隐藏错误消息,您实际上可能有机会调试您的程序。相反,您已经有效地将有用的错误消息信息替换为“哦,发生了错误。不过,我不会告诉您它是什么!”。如果您要将错误消息记录到表中,至少要确保它包含SQLERRM 和/或RAISE,否则记录发生错误的事实毫无意义。 【参考方案1】:

当您执行 open cursor for update 时,此语句适用于以下场景:首先运行 select 查询并为所有记录设置锁定,然后执行 fetch 操作。

PROCEDURE test(p_id IN NUMBER)
IS

  CURSOR cur_test IS
  SELECT emp_id,
         emp_name,
         rowid as row_id
  FROM  
         EMP;

  row_locked EXCEPTION;
  PRAGMA EXCEPTION_INIT(row_locked, -54);

  v_sql varchar2(4000) := 'SELECT 1 FROM EMP t WHERE rowid = :row_id FOR UPDATE NOWAIT';
  c int;
  n int;
BEGIN
  c := dbms_sql.open_cursor;
  dbms_sql.parse(c, v_sql, dbms_sql.native);

  FOR r_cur_test  IN cur_test 
  LOOP
    BEGIN
      dbms_sql.bind_variable (c, 'row_id', i.row_id);
      n := dbms_sql.execute(c);

      --do something

    EXCEPTION
      WHEN row_locked THEN
        --log locked record in log table
        log_lock('Record is locked');  
        COMMIT; 
    END;      
  END LOOP;

  dbms_sql.close_cursor(c);

  --call log function to log a successful run

  COMMIT;
EXCEPTION
  WHEN OTHERS THEN  
    --Unsuccessful completion of the run.Trap all unhandled exceptions.
    --log error in error table    
    log_lock('Process exited with error');   
    dbms_sql.close_cursor(c);
    ROLLBACK;

END;

【讨论】:

非常感谢您的详细回答。

以上是关于Oracle PL/SQL 在循环中捕获锁定异常并继续的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL 在 for 循环中执行即时异常处理

Oracle笔记4-pl/sql-分支/循环/游标/异常/存储/调用/触发器

抑制预期 Oracle 异常的 PHP 警告

动态 SQL/异常处理 -00942 - Oracle PL/SQL [重复]

循环中的 PL/SQL 异常

PL/SQL:捕获编号异常?