Исключение блокировки захвата Oracle PL/SQL в цикле и продолжение

У меня есть процедура ниже, которая перебирает курсор, выполняет некоторую логику. Я поставил 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, в результате чего процедура просто завершалась, не продолжая зацикливаться на курсоре.

Кто-нибудь может дать мне совет? Большое спасибо


person William Mu    schedule 28.11.2016    source источник
comment
Хорошо, а какое исключение в блоке когда другие?? Это беспорядок.   -  person OldProgrammer    schedule 28.11.2016
comment
После курсора для цикла есть еще код, и исключение в блоке when other предназначено для этого.   -  person William Mu    schedule 28.11.2016
comment
Ну что за исключение??? Если вы не можете предоставить всю необходимую информацию, как вы ожидаете, что кто-то вам поможет?   -  person OldProgrammer    schedule 28.11.2016
comment
Извините, я должен был быть более конкретным. Исключение в блоке OTHERS предназначено для обработки всех непредвиденных исключений.   -  person William Mu    schedule 28.11.2016
comment
Вы уверены, что исключение возникает, когда вы пытаетесь заблокировать записи. Я бы предложил на некоторое время удалить блок исключений и посмотреть, какое исключение возникает.   -  person phonetic_man    schedule 28.11.2016
comment
Да, я уверен. Я зарегистрировал жестко закодированное сообщение во временной таблице. Я обновил приведенный выше код соответственно. Спасибо   -  person William Mu    schedule 28.11.2016
comment
Я думаю, что исключение OTHERS возникает в начале цикла курсора for, поскольку оно заблокировано сеансом 1. Таким образом, оно никогда не попадало в цикл for, а исключение row_locked никогда не возникало.   -  person William Mu    schedule 28.11.2016
comment
@WilliamMu, если бы вы не скрывали сообщение об ошибке в своем блоке исключений, у вас действительно был бы шанс отладить свою процедуру. Вместо этого вы заменили полезную информацию об ошибке сообщением о том, что произошла ошибка. Я не собираюсь говорить вам, что это было, хотя!. Если вы собираетесь записывать сообщение об ошибке в таблицу, по крайней мере, убедитесь, что оно содержит SQLERRM и/или RAISE, иначе бессмысленно записывать факт возникновения ошибки.   -  person Boneist    schedule 28.11.2016
comment
Таким образом, любое исключение, кроме ORA-000054, передается внешнему обработчику «Другие» и регистрируется как «Процесс завершился с ошибкой» без какой-либо другой информации. Если вы хотите знать, что такое загадочное исключение, то, очевидно, не скрывайте его. Я не уверен, в чем вопрос.   -  person William Robertson    schedule 28.11.2016
comment
Спасибо, ребята, за все предложения. я попробую   -  person William Mu    schedule 29.11.2016


Ответы (1)


Когда вы делаете открываете курсор для обновления, этот оператор работает в сценарии: сначала запустите выберите запрос и установите блокировку для всех записей, а затем выполните выборку операции.

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;
person SkyWalker    schedule 28.11.2016
comment
Большое спасибо за подробный ответ. - person William Mu; 29.11.2016