Проблема заключается в том, что и ваша заявленная на работу процедура, и ваша внешняя программа могут вызывать nextval
между шагами 2 и 4; и мог позвонить в любом порядке; и потенциально внешняя программа может сделать несколько звонков.
Вы можете попытаться уменьшить это, изменив и процедуру, и внешний вызов.
В настоящий момент, если на шаге 3 произошла ошибка, процедура завершается, а шаг 4 так и не достигается, поэтому все будущие вызовы на nextval
продолжат с ошибкой. Таким образом, вы можете перехватывать и игнорировать ошибку ORA-08004, исходя из того, что вы получите только то, что если между шагами 2 и 3 произошел внешний вызов, последовательность была увеличена до нуля этим вызовом и, таким образом, пошагово 3 в данном сценарии фактически избыточен:
create or replace procedure reset_seq(p_seq_name in varchar2) is
l_val number;
e_8004 exception;
pragma exception_init(e_8004, -8004);
begin
execute immediate 'select ' || p_seq_name || '.nextval from dual'
INTO l_val; --1
execute immediate 'alter sequence ' || p_seq_name || ' increment by -' ||
l_val || ' minvalue 0'; --2
begin
execute immediate 'select ' || p_seq_name || '.nextval from dual'
INTO l_val; --3
exception
when e_8004 then
-- nextval has already been called by someone else
null;
end;
execute immediate 'alter sequence ' || p_seq_name ||
' increment by 1 minvalue 0'; --4
end;
Теперь, если внешняя программа вызывает nextval
между шагами 2 и 3, вы получите ошибку на шаге 3, но проигнорируете ее, и шаг 4 все равно будет выполнен.
Ваша внешняя программа получит значение nextval
, равное нулю, если она выполнит вызов между шагами 2 и 3; и получит ORA-08004, если он вызывает между шагами 3 и 4 - или если он делает несколько вызовов в окне между шагами 2 и 4. Итак, предположим, что вы не ожидаете, что ноль будет действительным результатом (что кажется разумным, так как как правило, вы этого никогда не получите), вы можете совершать повторные звонки, пока не получите ненулевой ответ и без ошибок. В псевдокоде что-то вроде:
loop
val = seq.nextval;
if error == -8004 then continue;
if val == 0 then continue;
break;
end loop
Вы могли бы подумать о том, чтобы поместить эту логику в функцию PL / SQL и заставить вашу программу вызывать функцию вместо прямого доступа к последовательности, чтобы скрыть эту сложность и избежать необходимости повторять ее, если потенциально затрагивается более одной программы. .