次の制約を持つ次のテーブルがあるとします。
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
?最初にs 、UPDATE
次にINSERT
s の場合、インデックス/制約は実行中に無効化されません。しかし、最初INSERT
に s を実行してからUPDATE
s を実行すると、インデックスが一時的に無効になり、ステートメントが失敗する可能性があります。
Oracle RDBMSがそのような問題をどのように処理するかを誰かが詳細に説明できますか(または正しい方向を指し示すことができますか)? また、LOG ERRORS INTO
句を使用する場合の扱いは同じですか?
この質問をする主な理由と解決策が必要な理由: LOG ERRORS INTO 句を使用して MERGE ステートメントを数時間実行しています。エラーログは、自律的なトランザクションとして機能しているようです。いくつかの一意の制約エラー (一意のインデックスに基づく) は、ステートメントが upserting を完了するずっと前にログに記録され (特に、シーケンスが上に行くのが見えます)、その理由はわかりません (ただし、upserting の後、一意の制約は存在しないはずです)。無効)。ERROR テーブルを調べると、ORA-00001: INSERT 操作で一意の制約 (XXX.YYY) に違反しています。このレコードを ERROR テーブルからメイン テーブルに挿入しても、一意制約エラーは発生しません。そもそもなぜエラーがログに記録されるのだろうか。
編集:以下の回答は、ステートメントが実行されると、ステートメントの最後に制約が適用されることを主張しています。私は理解し、同意します (そのようなシナリオでのインデックスのメンテナンスについて詳しく知りたいのですが)。私が理解していないこと、およびこの質問がまだ回答されていない理由は、これらの ORA-00001: 一意の制約 (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 ステートメント (本番環境) は PARALLEL では機能しなくなりましたが、それでも時々このエラーが発生します...
EDIT4: 質問自体は完全には回答されていませんが、私が承認済みとしてマークした最良の回答。Oracle の単なるバグのようです。