6

次の制約を持つ次のテーブルがあるとします。

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次にINSERTs の場合、インデックス/制約は実行中に無効化されません。しかし、最初INSERTに s を実行してからUPDATEs を実行すると、インデックスが一時的に無効になり、ステートメントが失敗する可能性があります。

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 の単なるバグのようです。

4

2 に答える 2

5

このマージは決して失敗しません。

これについては、次の例で説明します:データベースの概念 - 5. データの整合性

遅延不可能な制約の場合 (デフォルト):

遅延不可能な制約では、Oracle Databaseは制約の有効性チェックをトランザクションの最後まで遅延しません。代わりに、データベースは各ステートメントの最後で制約をチェックします。制約に違反した場合、ステートメントはロールバックします。



上記は、制約が単一の SQL ステートメント全体の最後にチェックされることを意味しますが、実行中はチェックされません。



以下のこのドキュメントでは、実行中に「内部的に」いくつかの制約ルールに違反するトランザクションの 2 つの例を見つけることができますが、最終的にはすべての制約を満たし、正当なものがあります。

...ステートメントが完了した後、データベースは制約を効果的にチェックするためです。図5-4は、データベースが制約をチェックする前にSQL文全体のアクションを実行することを示しています。

最後に、彼らは次のようにも書いています。

このセクションの例は、INSERT および UPDATE ステートメント中の制約チェック メカニズムを示していますが、データベースはすべてのタイプの DML ステートメントに対して同じメカニズムを使用します。自己参照制約だけでなく、すべてのタイプの制約に同じメカニズムが使用されます。

于 2014-12-10T18:19:58.193 に答える
4

ジョブの「LOG ERRORS INTO」部分は、他のユーザーが指摘したように、ステートメントが実行された後 (更新および挿入部分)、制約のチェック中に発生します。そのため、制約チェックが完了する前にエラーを挿入できます。これが、ステートメントが完全に終了する前に挿入されたエラーが表示される理由です。

そして、この観察に対する答えとして:

このレコードを ERROR テーブルからメイン テーブルに挿入しても、一意制約エラーは発生しません。そもそもなぜエラーがログに記録されるのだろうか。

1 つの Merge ステートメントにすべての情報が含まれていることを確認してください。同じステートメントで値を更新しないが、失敗した挿入と再試行の間に発生する別のステートメントで値を更新しない場合、物事は説明可能です。

(私が言いたいのは、USING 部分のレコードが同じステートメントにないということです。

  • セッション 1: merge using select 4 as id, 'b' as name from dual (エラーがログに挿入されます)
  • セッション 2: select 2 as id, null as name from dual commit okを使用したマージ
  • セッション 3: 挿入を再試行すると、機能します

)

1 つのステートメントでエラーを再現できる場合、それは問題になります。しかし、あなたの環境には多くのセッションがあります。Merge ステートメントのソースを確認してください。到着が遅れたり、このようなことがあります。

于 2015-03-20T11:49:33.000 に答える