Вот небольшой пример, который показывает, почему вы читаете строки дважды.
Запустите следующий код в двух сеансах, начиная второй через несколько секунд после первого:
declare
cursor c is
select a.*
from TST_SAMPLE A
where rownum < 10
and NOT EXISTS (select null
from TST_SAMPLE_STATUS B
where B.rec_id = A.rec_id)
FOR UPDATE SKIP LOCKED;
type rec is table of c%rowtype index by pls_integer;
rws rec;
begin
open c; -- data are read consistent to this time
dbms_lock.sleep ( 10 );
fetch c
bulk collect
into rws;
for i in 1 .. rws.count loop
dbms_output.put_line ( rws(i).rec_id );
end loop;
commit;
end;
/
Вы должны увидеть, что оба сеанса отображают одинаковые строки.
Почему?
Поскольку база данных Oracle имеет согласованность на уровне операторов, при открытии курсора набор результатов для обоих будет заморожен.
Но когда у вас заблокирован SKIP, блокировка FOR UPDATE включается только , когда вы выбираете строки .
Итак, сессия 1 начинается и находит первые 9 строк, не входящих в TST_SAMPLE_STATUS.Затем он ждет 10 секунд.
Если вы начнете сеанс 2 в течение этих 10 секунд, курсор будет искать те же девять строк .
В этот момент никакие строки не заблокированы.
Теперь вот где это становится интересным.
Сон в первом сеансе закончится.Затем он извлекает строки, блокируя их и пропуская все, что уже заблокировано.
Очень скоро после этого он совершит. Снятие блокировки .
Через несколько секунд сеанс 2 начинает читать эти строки.На данный момент строки не заблокированы !
Так что пропускать нечего.
То, как именно вы решите это, зависит от того, что вы пытаетесь сделать.
Предполагается, что вы не можете перейти на набор, основанный на множестве.подход, вы можете сделать транзакции сериализуемыми, добавив:
set transaction isolation level serializable;
перед циклом курсора.Затем он перейдет к согласованности на уровне транзакций.Включение базы данных для обнаружения «что-то изменилось» при получении строк.
Но вам нужно поймать ORA-08177: can't serialize access for this transaction
ошибок в вашем внешнем цикле.Или любой процесс, который перечитывает те же строки, в этот момент выпадет.
Или, как предложили комментаторы, использовали Advanced Queuing.