Инструкция MERGE уникальный индекс / проверка ограничений для каждой строки или для каждой инструкции?

Предположим, у меня есть следующая таблица со следующими ограничениями:

create table test as (
    select 1 as id, 'a' as name from dual 
    union all 
    select 2, 'b' from dual 
    union all 
    select 3, 'c' from dual
);

create unique index ind on test(name);

alter table test add constraint constr unique (name);

select * from test;

        ID NAME
---------- ----
         1 a   
         2 b   
         3 c   

предположим теперь, что я делаю следующее MERGE:

merge into test t using (
    select 4 as id, 'b' as name from dual 
    union all 
    select 2 as id, null as name from dual 
) s on (s.id = t.id) 
    when matched then update set t.name = s.name
    when not matched then insert(t.id, t.name) values(s.id, s.name)

select * from test;

        ID NAME
---------- ----
         1 a   
         2     
         3 c   
         4 b   

будет выше MERGE когда-нибудь сбой? Если это UPDATEs сначала, а затем INSERTs, индекс / ограничение не будет признано недействительным во время выполнения. Но если он первый INSERTи UPDATEs, индекс будет временно недействительным, и оператор может завершиться ошибкой?.

Can кто-то подробно объясняет (или указывает в правильном направлении), как Oracle RDBMS обрабатывает такие проблемы? Кроме того, обработка одинакова при использовании LOG ERRORS INTO предложения?

главная причина, почему я задаю этот вопрос и почему мне нужно решение: у меня есть операторы слияния, работающие в течение нескольких часов с ошибками журнала в предложение. Журнал ошибок, похоже, работает как автономная транзакция. Некоторые уникальные ошибки ограничений (на основе уникальных индексов) регистрируются намного раньше оператор заканчивает upserting (среди прочего, я вижу последовательность, идущую вверх), и я не знаю, почему (хотя в конце концов, после upserting, никакое уникальное ограничение не должно быть недействительным). Когда я смотрю в таблицу ошибок, я вижу Ora-00001: unique constraint (XXX.YYY) нарушается при операции вставки. Я могу вставить эту запись из таблицы ошибок в главную таблицу, не вызывая сбоя уникального ограничения. Поэтому мне интересно, почему ошибка регистрируется в первом место.

EDIT: в приведенных ниже ответах утверждается, что при выполнении оператора ограничения применяются в конце оператора. Я понимаю и согласен (в то время как я хотел бы знать больше подробностей о поддержании индекса в таких сценариях). Чего я не понимаю и почему на этот вопрос до сих пор не ответили, так это почему у меня есть эти Ora-00001: unique constraint (XXX.YYY) нарушенные ошибки регистрируются, пока их не должно быть. Выглядит как Ошибка механизма лесозаготовки не ведет себя атомарно.

EDIT2:

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE    11.2.0.4.0  Production
TNS for Solaris: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

EDIT3: Я немного поиграл и смог воспроизвести эту ошибку:

drop table test;

drop table err_test;

create table test as (
    select 1 as id, 'a' as name from dual 
    union all 
    select 2, 'b' from dual 
    union all 
    select 3, 'c' from dual
);

create unique index ind on test(name);

alter table test add constraint constr unique (name);

--select test.rowid, test.* from test;

BEGIN
DBMS_ERRLOG.CREATE_ERROR_LOG (
   dml_table_name            => 'TEST',
   err_log_table_name        => 'ERR_TEST');
END;
/

--truncate table err_test;

select * from err_test;

merge /*+ PARALLEL(t 2) */ into test t using (
    select 4 as id, 'b' as name from dual 
    union all 
    select 2 as id, null as name from dual 
) s on (s.id = t.id) 
    when matched then update set t.name = s.name
    when not matched then insert(t.id, t.name) values(s.id, s.name)
LOG ERRORS INTO ERR_TEST('TEST,ID:'||s.id) REJECT LIMIT UNLIMITED;

select * from err_test;

в прошлом select * from err_test; Я всегда получаю: ORA-00001: unique constraint (XXX.CONSTR) violated. Теперь странно, что реальный оператор MERGE (в производстве) больше не работает параллельно, и я все еще получаю эту ошибку иногда...

EDIT4: Лучший ответ, который я отметил как принятый, хотя сам вопрос не получил полного ответа. Кажется, это просто ошибка в Oracle.

2 ответов


это слияние никогда не подводит.

Это объясняется примерами здесь:Концепции Базы Данных-5. Целостность Данных

для не defferrable ограничений (по умолчанию):

в nondeferrable ограничения, базы данных Oracle никогда не откладывает проверка действительности ограничения до конца транзакции. Вместо базы данных проверяет ограничение в конце каждого заявление. если ограничение нарушено, то заявление роллы спина.



Вышеизложенное означает, что ограничения проверяются в конце всего одного оператора SQL, но не во время их выполнения.



Ниже, в этой документации, вы можете найти два примера транзакций, которые "внутри", во время их выполнения, нарушают некоторые правила ограничения, но в конце они выполняют все ограничения, и есть законные, потому что:

... поскольку база данных эффективно проверяет ограничения после оператор завершает. На рис. 5-4 показано, что база данных выполняет действия всей инструкции SQL перед проверкой ограничений.

в конце концов они также написали, что:

примеры в этом разделе иллюстрируют проверка ограничений механизм во время инструкций INSERT и UPDATE, но база данных использует тот же механизм для всех типов операторов DML. Тот же механизм используется для всех типов ограничений, а не только для самореферентных ограничения.


" ошибки журнала в " часть задания, как указывали другие пользователи, происходит после выполнения инструкции (обновить и вставить часть), пока проверка ограничений. Таким образом, вы можете вставлять ошибки до завершения проверки ограничений. Вот почему вы видите ошибку, вставленную до того, как оператор будет полностью завершен.

и в качестве ответа на это замечание:

Я могу вставить эту запись из таблицы ошибок в основную таблицу без вызывает сбой уникального ограничения. Поэтому мне интересно, почему ошибка регистрируется во-первых.

убедитесь, что у вас есть вся информация в одном операторе слияния. если вы не обновляете значение в том же операторе, но в другом, которое происходит между неудачной вставкой и повторной попыткой, все объяснимо.

(Я имею в виду, что записи в части USING не находятся в одном и том же операторе.

  • сеанс 1: объединить с помощью select 4 as id, 'b' as name from dual (ошибка вставляется в журнал)
  • сессия 2: слияние с помощью select 2 as id, null as name from dual commit ok
  • сеанс 3: повторите попытку вставьте и он работает

)

Если вы можете воспроизвести ошибку с помощью одного оператора, это будет проблемой. Но у вас много сеансов в вашем окружении. Пожалуйста, проверьте источник ваших заявлений о слиянии. Вы можете опоздать или что-то в этом роде.