Инструкция 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
когда-нибудь сбой? Если это UPDATE
s сначала, а затем INSERT
s, индекс / ограничение не будет признано недействительным во время выполнения. Но если он первый INSERT
и UPDATE
s, индекс будет временно недействительным, и оператор может завершиться ошибкой?.
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: повторите попытку вставьте и он работает
)
Если вы можете воспроизвести ошибку с помощью одного оператора, это будет проблемой. Но у вас много сеансов в вашем окружении. Пожалуйста, проверьте источник ваших заявлений о слиянии. Вы можете опоздать или что-то в этом роде.