3

Oracle MERGE INTO... DML ステートメントを使用してテーブル A を更新し、別のテーブル B の一部の変更に対応するアプリケーションがあります (テーブル A は、テーブル B の選択された部分とその他の情報をまとめたものです)。典型的なマージ操作では、5 ~ 6 行 (数万行のうち) がテーブル B に挿入され、2 ~ 3 行が更新されます。

アプリケーションは、ターゲット テーブルにセキュリティ ポリシーが設定されている環境にデプロイされることが判明しました。MERGE INTO...文はこれらの表では使用できません(ORA-28132: Merge into構文はセキュリティ・ポリシーをサポートしていません)

そのため、代わりに通常の挿入と更新を使用するように MERGE INTO... ロジックを変更する必要があります。これは他の誰かが遭遇した問題ですか?マージ ステートメントの WHEN MATCHED/WHEN NOT MATCHED ロジックを INSERT および UPDATE ステートメントに変換するためのベスト プラクティス パターンはありますか? マージはストアド プロシージャ内で行われるため、必要に応じてソリューションで DML に加えて PL/SQL を使用しても問題ありません。

4

2 に答える 2

1

Another way to do this (other than Merge) would be using two sql statements one for insert and one for update. The "WHEN MATCHED" and "WHEN NOT MATCHED" can be handled using joins or "in" Clause.

If you decide to take the below approach, it is better to run the update first (sine it only runs for the matching records) and then insert the non-Matching records. The Data sets would be the same either way, it just updates less number of records with the order below.

Also, Similar to the Merge, this update statement updates the Name Column even if the names in Source and Target match. If you dont want that, add that condition to the where as well.

create table src_table(
   id number primary key,
   name varchar2(20) not null
);

create table tgt_table(
   id number primary key,
   name varchar2(20) not null
);

insert into src_table values (1, 'abc');
insert into src_table values (2, 'def');
insert into src_table values (3, 'ghi');

insert into tgt_table values (1, 'abc');
insert into tgt_table values (2,'xyz');

SQL> select * from Src_Table;

        ID NAME
---------- --------------------
         1 abc
         2 def
         3 ghi

SQL> select * from Tgt_Table;

        ID NAME
---------- --------------------
         2 xyz
         1 abc

Update tgt_Table tgt
   set Tgt.Name = 
      (select Src.Name
          from Src_Table Src
          where Src.id = Tgt.id
      );

2 rows updated. --Notice that ID 1 is updated even though value did not change

select * from Tgt_Table;

   ID NAME
----- --------------------
    2 def
    1 abc

insert into tgt_Table
select src.*
  from Src_Table src,
       tgt_Table tgt
  where src.id = tgt.id(+)
    and tgt.id is null;

1 row created.

SQL> select * from tgt_Table;

        ID NAME
---------- --------------------
         2 def
         1 abc
         3 ghi

commit;

There could be better ways to do this, but this seems simple and SQL-oriented. If the Data set is Large, then a PL/SQL solution won't be as performant.

于 2011-06-15T21:47:48.013 に答える
0

私があまり知らないセキュリティ ポリシーを掘り下げる以外に、少なくとも 2 つの選択肢が考えられます。

レコードを処理して行ごとにマージします。ほとんどのレコードが更新または挿入を必要とするかどうかに応じて、更新に失敗した場合は更新を試みてから挿入するか、またはその逆を行います (つまり、実行される SQL ステートメントの数を減らす最も一般的なケースに合わせて最適化します)。 :

begin
  for row in (select ... from source_table) loop
    update table_to_be_merged
    if sql%rowcount = 0 then -- no row matched, so need to insert
      insert ...
    end if;
  end loop;
end;

別のオプションは、配列にマージするレコードを一括収集してから、それらを一括挿入して、すべての主キーの例外をキャッチすることです (現在、この構文を思い出すことはできませんが、一括挿入を取得できます挿入に失敗したすべての行を別の配列に配置してから処理します)。

論理的には、merge ステートメントは舞台裏で各レコードの存在をチェックする必要があり、上記のコードと非常によく似た方法で処理されると思います。ただし、マージは常に PLSQL でコーディングするよりも効率的です。多くの SQL 呼び出しではなく 1 つの SQL 呼び出しだけになるからです。

于 2011-06-15T21:16:29.910 に答える