PL/SQL Выполнить немедленную обработку исключений внутри цикла for

В приведенном ниже коде PL/SQL TABLE_ONE содержит имя таблицы tname , имя столбца cname и идентификатор строки rid. Цикл For извлекает записи из TABLE_ONE и обновляет столбец cname в таблице tname для записи с идентификатором строки 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;

person user194210    schedule 21.11.2012    source источник


Ответы (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;
person Kevin Seymour    schedule 08.09.2017