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

Posted

技术标签:

【中文标题】PL/SQL 在 for 循环中执行即时异常处理【英文标题】:PL/SQL Execute immediate exception handling inside for loop 【发布时间】:2012-11-21 04:02:57 【问题描述】:

在下面的 PL/SQL 代码中,TABLE_ONE 包含表名 tname 、列名 cname 和 rowid rid。 For 循环从 TABLE_ONE 获取记录并更新表 tname 中的列 cname 以获取行 ID 为 rid 的记录。但是如果 tname 中要更新的记录被锁定,那么 for 循环就会卡住,并且不会处理来自 TABLE_ONE 的更多记录。理想情况下,脚本忽略更新失败的记录并继续进行。请告知可能是什么问题。

BEGIN
    FOR c IN (SELECT * FROM TABLE_ONE a )
    LOOP
        DECLARE
            TNAME varchar2(30);
            CNAME varchar2(30);
            RID ROWID;
            X number;
            updt_stmt varchar2(300);
        BEGIN
          BEGIN
            TNAME := c.TNAME;
            CNAME := c.CNAME;
            RID   := c.RID;                
            DBMS_OUTPUT.PUT_LINE( TNAME || '=>' || CNAME);
            updt_stmt := 'UPDATE ' || TNAME || ' SET ' || CNAME || ' = ''123'' WHERE  ROWID   like ''%' || RID || '%''';                
            EXECUTE IMMEDIATE updt_stmt;

          EXCEPTION
            WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE('ERROR');

          END;
        END;
    END LOOP;
END;

【问题讨论】:

【参考方案1】:

未经测试,但我认为您可以先尝试使用 FOR UPDATE 锁定记录,如果其他事务处于活动状态,则指定 NOWAIT 会导致失败。然后,您可以捕获此异常并跳过处理。这是一个未经测试的示例:

DECLARE
  x ROWID;
  resource_busy EXCEPTION;
  PRAGMA EXCEPTION_INIT(resource_busy,
                        -00054);
BEGIN
  FOR c IN (SELECT *
              FROM table_one a)
  LOOP
    DECLARE
      tname     VARCHAR2(30);
      cname     VARCHAR2(30);
      rid       ROWID;
      x         NUMBER;
      updt_stmt VARCHAR2(300);
    BEGIN
      BEGIN
        tname := c.tname;
        cname := c.cname;
        rid   := c.rid;
        dbms_output.put_line(tname || '=>' || cname);

        BEGIN
          EXECUTE IMMEDIATE 'SELECT rowid FROM ' || tname ||
                            ' WHERE rowid = :x FOR UPDATE NOWAIT'
            INTO x
            USING rid;
        EXCEPTION
          WHEN resource_busy THEN
            dbms_output.put_line('Record locked; try again later.');
            CONTINUE;
        END;

        updt_stmt := 'UPDATE ' || tname || ' SET ' || cname ||
                     ' = ''123'' WHERE  ROWID   like ''%' || rid || '%''';
        EXECUTE IMMEDIATE updt_stmt;

      EXCEPTION
        WHEN OTHERS THEN
          dbms_output.put_line('ERROR');

      END;
    END;
  END LOOP;
END;

【讨论】:

以上是关于PL/SQL 在 for 循环中执行即时异常处理的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL 错误处理后继续执行

如何以不同的方式处理 PL/SQL 中的不同异常?

实验七:PL/SQL编程基础

zbb20170601 oracle PL/SQL 语句块 游标 自定义游标 异常处理EXCEPTION

PL/SQL loop循环详解

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