Да, именно так и происходит.Время должно быть правильным, поэтому оно прерывистое.Вы можете убедиться в этом, если откроете два сеанса sqlplus.
- Сессия A: Обновления, строки не изменены, поэтому
SQL%ROWCOUNT
= 0. - Сессия B: Обновления, строки не изменены, поэтому
SQL%ROWCOUNT
= 0. - Сеанс A: проходит условие
IF
, вставка тоже. - Сессия B: проходит условие
IF
, вставка тоже. - Сессия A: фиксирует.
- Сессия B: фиксирует.К сожалению, есть нарушение ограничения, потому что теперь я вижу изменения сеанса A.
Изменение уровня изоляции вам не поможет.Oracle не имеет никакого уровня изоляции, который позволял бы вам видеть незафиксированные изменения из другого сеанса (и это хорошо).
Первое, что нужно сделать, это поменять UPDATE
и INSERT
на оператор MERGE
.Таким образом, у вас есть только одно утверждение, которое будет успешным или неудачным.Насколько я понимаю, вставка и обновление, разделенные условием, являются антипаттерном.Примерно так:
MERGE INTO customer
USING ( SELECT customer_name FROM wherever ) source
ON ( source.customer_name = customer.customer_name )
WHEN NOT MATCHED THEN INSERT VALUES ( source.customer_name )
WHEN MATCHED THEN UPDATE SET ( customer_name = source.customer_name );
Недостаток MERGE
заключается в том, что в нем нет предложения RETURNING INTO
, поэтому, если вам это нужно, вы должны использовать команду select / insert.
Во-вторых, чтобы остановить одновременную вставку двух сессий, вам нужно сделать:
LOCK TABLE customer IN SHARE MODE;
Мне вообще не нравится делать пользовательские блокировки, но я не знаю другихспособ обойти это.Это остановит другие сеансы от изменения таблицы до тех пор, пока не будет зафиксирован первый сеанс, хотя они могут запросить ее.Это означает, что доступ к таблице сериализуется, поэтому, если у вас много сеансов, пытающихся обновить эту таблицу, это может быть неприемлемо.