MERGE
目的
MERGE
文を使用すると、1つ以上のソースから行を選択し、表またはビューに対して更新および挿入できます。対象となる表またはビューに対して更新と挿入のどちらを実行するかを決定する条件を指定できます。
この文は、複数の操作を組み合せるときに便利です。DML文INSERT
、UPDATE
およびDELETE
を複数指定する必要がなくなります。
MERGE
は、決定的な文です。対象となる表の同じ行を、同一のMERGE
文で何度も更新することはできません。
ノート:
Oracle Databaseの以前のリリースでは、MERGE
INTO
文を含むアプリケーションでOracle Virtual Private Databaseポリシーを作成すると、Virtual Private Databaseポリシーが存在するため、MERGE
INTO
文はORA-28132「MERGE INTO構文ではセキュリティ・ポリシーをサポートしていません」
エラーにより回避されていました。Oracle Database 11gリリース2(11.2.0.2)以降では、MERGE
INTO
操作を含むアプリケーションでポリシーを作成できます。そのためには、DBMS_RLS
.ADD_POLICY
statement_types
パラメータにINSERT
、UPDATE
およびDELETE
文を含めるか、statement_types
パラメータを完全に省略します。特定のタイプのSQL文にポリシーを適用する方法の詳細は、『Oracle Databaseセキュリティ・ガイド』を参照してください。
前提条件
対象となる表に対するINSERT
オブジェクト権限とUPDATE
オブジェクト権限、およびソース・オブジェクトに対するSELECT
オブジェクト権限が必要です。merge_update_clause
のDELETE
句を指定するには、対象となる表またはビューに対するDELETE
オブジェクト権限も必要です。
構文
merge::=
ノート:
少なくともmerge_update_clause
またはmerge_insert_clause
句のどちらかを指定する必要があります。
(merge_update_clause::=、merge_insert_clause::=、error_logging_clause::=)
values_clause::=
where_clause::=
error_logging_clause::=
セマンティクス
INTO句
INTO
句を使用すると、更新または挿入の対象となる表またはビューを指定できます。データをビューにマージする場合、そのビューは更新可能であることが必要です。詳細は、「更新可能なビューのノート」を参照してください。
ターゲット・ビューの制限事項
INSTEAD
OF
トリガーが定義されたターゲット・ビューは指定できません。
USING句
USING
句を使用して、更新または挿入の元となるソースを指定します。
values_clause
values_clause
のセマンティクスについては、SELECT
文の「values_clause」のvalues_clause
を参照してください。
ON句
ON
句を使用すると、MERGE
の更新操作または挿入操作の条件を指定できます。対象となる表の中で検索条件が真となる各行は、ソースの対応するデータに基づいて行が更新されます。どの行も条件が真とならない場合、ソースの対応する行に基づいて対象となる表に行が挿入されます。
merge_update_clause
merge_update_clause
を指定すると、対象となる表またはビューの新しい列値を指定できます。ON
句の条件が真となる場合、更新が実行されます。更新が実行されると、対象となる表に定義されているすべての更新トリガーがアクティブになります。
where_clause
を指定すると、指定した条件が真の場合のみに更新操作が実行されるようにできます。条件には、データソースまたは対象となる表を参照できます。条件が真ではない場合、行を表に挿入する際に更新操作がスキップされます。
DELETE
where_clause
を指定すると、表の移入中または更新中にその表内のデータをクリーンアップできます。この句によって処理される行は、マージ操作によって更新される対象の表内の行のみです。DELETE
WHERE
条件は、更新後の値を評価し、UPDATE
SET
... WHERE
条件によって評価された元の値は評価しません。更新先の表の行がDELETE
条件を満たし、ON
句によって定義された結合に含まれていない場合、その行は削除されます。更新先の表に定義されている削除トリガーが起動し、各行が削除されます。
この句は、単独で、またはmerge_insert_clause
とともに指定できます。merge_insert_clauseとともに指定する場合は、どちらを先に指定してもかまいません。
merge_update_clauseの制限事項
この句には、次の制限事項があります。
-
参照する列は、
ON
condition
句で更新できません。 -
ビューを更新する場合は、
DEFAULT
を指定できません。
merge_insert_clause
merge_insert_clause
を指定すると、ON
句の条件が偽となる場合に対象となる表の列に挿入する値を指定できます。挿入が実行されると、対象となる表に定義されているすべての挿入トリガーがアクティブになります。INSERT
キーワードの後に列リストを指定しない場合、対象となる表内の列数は、VALUES
句内の値の数と一致している必要があります。
すべてのソース行を表に挿入するには、ON
句の条件に定数フィルタ条件を使用します。定数フィルタ条件の一例はON
(0=1
)です。Oracle Databaseはこのような条件を認識すると、すべてのソース行を無条件に表に挿入します。この方法は、merge_update_clause
を省略することとは異なります。merge_update_clauseを省略しても、結合は実行されます。定数フィルタ条件を設定すると、結合は実行されません。
where_clause
を指定すると、指定した条件が真の場合のみに更新操作が実行されるようにできます。条件には、データソース列のみを参照できます。条件が真ではないすべての行に対する挿入操作はスキップされます。
merge_insert_clause
は、単独で、またはmerge_update_clause
とともに指定できます。merge_insert_clauseとともに指定する場合は、どちらを先に指定してもかまいません。
merge_insert_clauseの制限事項
ビューに挿入する場合は、DEFAULT
を指定できません。
error_logging_clause
error_logging_clauseのMERGE
文での動作は、INSERT
文の場合と同じです。詳細は、「INSERT
」文の「error_logging_clause」を参照してください。
関連項目:
例
表へのマージ: 例
次の例では、サンプル・スキーマoe
のデフォルト・ボーナスが100であるbonuses
表を使用します。次に、oe.orders
表のsales_rep_id
列に基づいて、販売実績があったすべての従業員をbonuses
表に挿入します。最終的に、人事部門マネージャが、給与が$8000以下の従業員にボーナスを支給することを決定します。販売実績がなかった従業員には、給与の1%がボーナスとして支給されます。販売実績があった従業員には、給与の1%がボーナスに加算されて支給されます。MERGE
文は、これらの変更を1行で実装します。
CREATE TABLE bonuses (employee_id NUMBER, bonus NUMBER DEFAULT 100); INSERT INTO bonuses(employee_id) (SELECT e.employee_id FROM hr.employees e, oe.orders o WHERE e.employee_id = o.sales_rep_id GROUP BY e.employee_id); SELECT * FROM bonuses ORDER BY employee_id; EMPLOYEE_ID BONUS ----------- ---------- 153 100 154 100 155 100 156 100 158 100 159 100 160 100 161 100 163 100 MERGE INTO bonuses D USING (SELECT employee_id, salary, department_id FROM hr.employees WHERE department_id = 80) S ON (D.employee_id = S.employee_id) WHEN MATCHED THEN UPDATE SET D.bonus = D.bonus + S.salary*.01 DELETE WHERE (S.salary > 8000) WHEN NOT MATCHED THEN INSERT (D.employee_id, D.bonus) VALUES (S.employee_id, S.salary*.01) WHERE (S.salary <= 8000); SELECT * FROM bonuses ORDER BY employee_id; EMPLOYEE_ID BONUS ----------- ---------- 153 180 154 175 155 170 159 180 160 175 161 170 164 72 165 68 166 64 167 62 171 74 172 73 173 61 179 62
条件付き挿入と更新: 例
次の例では、MERGE
文を使用して、条件に従って表データを挿入し更新します。
次の文は、people_source
とpeople_target
という名前の2つの表を作成し、名前を移入します。
CREATE TABLE people_source (
person_id INTEGER NOT NULL PRIMARY KEY,
first_name VARCHAR2(20) NOT NULL,
last_name VARCHAR2(20) NOT NULL,
title VARCHAR2(10) NOT NULL
);
CREATE TABLE people_target (
person_id INTEGER NOT NULL PRIMARY KEY,
first_name VARCHAR2(20) NOT NULL,
last_name VARCHAR2(20) NOT NULL,
title VARCHAR2(10) NOT NULL
);
INSERT INTO people_target VALUES (1, 'John', 'Smith', 'Mr');
INSERT INTO people_target VALUES (2, 'alice', 'jones', 'Mrs');
INSERT INTO people_source VALUES (2, 'Alice', 'Jones', 'Mrs.');
INSERT INTO people_source VALUES (3, 'Jane', 'Doe', 'Miss');
INSERT INTO people_source VALUES (4, 'Dave', 'Brown', 'Mr');
COMMIT;
次の文は、person_id
列を使用してpeople_target
とpeople_source
の内容を比較します。people_source
表に一致するものがあると、people_target
表の値が更新されます。
MERGE INTO people_target pt
USING people_source ps
ON (pt.person_id = ps.person_id)
WHEN MATCHED THEN UPDATE
SET pt.first_name = ps.first_name,
pt.last_name = ps.last_name,
pt.title = ps.title;
次の文は、people_target
表の内容を表示し、ロールバックを実行します。
SELECT * FROM people_target;
PERSON_ID FIRST_NAME LAST_NAME TITLE
---------- -------------------- -------------------- ----------
1 John Smith Mr
2 Alice Jones Mrs.
ROLLBACK;
次の文は、person_id
列を使用してpeople_target
表とpeople_source
表の内容を比較します。people_source
表に一致するものがある場合にのみ、people_target
表の値が更新されます。
MERGE INTO people_target pt
USING people_source ps
ON (pt.person_id = ps.person_id)
WHEN NOT MATCHED THEN INSERT
(pt.person_id, pt.first_name, pt.last_name, pt.title)
VALUES (ps.person_id, ps.first_name, ps.last_name, ps.title);
次の文は、people_target
表の内容を表示し、ロールバックを実行します。
SELECT * FROM people_target;
PERSON_ID FIRST_NAME LAST_NAME TITLE
---------- -------------------- -------------------- ----------
1 John Smith Mr
2 alice jones Mrs
3 Jane Doe Miss
4 Dave Brown Mr
ROLLBACK;
次の文は、person_id
列を使用してpeople_target
表とpeople_source
表の内容を比較し、people_target
表で条件に従ってデータを挿入し更新します。people_source
表の一致する行ごとに、people_source
表の値を使用してpeople_target
表の値が更新されます。people_source
表の一致しない行は、people_target
表に追加されます。
MERGE INTO people_target pt
USING people_source ps
ON (pt.person_id = ps.person_id)
WHEN MATCHED THEN UPDATE
SET pt.first_name = ps.first_name,
pt.last_name = ps.last_name,
pt.title = ps.title
WHEN NOT MATCHED THEN INSERT
(pt.person_id, pt.first_name, pt.last_name, pt.title)
VALUES (ps.person_id, ps.first_name, ps.last_name, ps.title);
次の文は、people_target
表の内容を表示し、ロールバックを実行します。
SELECT * FROM people_target;
PERSON_ID FIRST_NAME LAST_NAME TITLE
---------- ------------- ------------------ ----------
1 John Smith Mr
2 Alice Jones Mrs.
3 Jane Doe Miss
4 Dave Brown Mr
ROLLBACK;
次の文は、person_id
列を使用してpeople_target
表とpeople_source
表を比較します。person_id
が一致すると、people_source
表の値を使用して、people_target
表で対応する行が更新されます。DELETE
句は、title
が‘Mrs.’であるpeople_target
のすべての値を削除します。person_id
が一致しない場合は、people_source
表の行がpeople_target
表に追加されます。WHERE
句を指定しているので、title
が‘Mr’である値のみがpeople_target
表に追加されます。
MERGE INTO people_target pt
USING people_source ps
ON (pt.person_id = ps.person_id)
WHEN MATCHED THEN UPDATE
SET pt.first_name = ps.first_name,
pt.last_name = ps.last_name,
pt.title = ps.title
DELETE where pt.title = 'Mrs.'
WHEN NOT MATCHED THEN INSERT
(pt.person_id, pt.first_name, pt.last_name, pt.title)
VALUES (ps.person_id, ps.first_name, ps.last_name, ps.title)
WHERE ps.title = 'Mr';
次の文は、people_target
表の内容を表示し、ロールバックを実行します。
SELECT * FROM people_target;
PERSON_ID FIRST_NAME LAST_NAME TITLE
---------- -------------------- -------------------- ----------
1 John Smith Mr
4 Dave Brown Mr
ROLLBACK;
アプリケーションからの入力の処理
通常、アプリケーションでは、新しい行をINSERT
するか、既存の行をUPDATE
するかを決定するために、最初に行の存在をチェックする必要があります。MERGE
文では、USING
文内のバインド変数をソースとして使用できるため、このようなチェックが不要になります。
次の文は、バインド変数を使用して新しい行をpeople_target
表に挿入する方法を示しています。
var person_id NUMBER;
var first_name VARCHAR2(20);
var last_name VARCHAR2(20);
var title VARCHAR2(10);
exec :person_id := 3;
exec :first_name := 'Gerald';
exec :last_name := 'Walker';
exec :title := 'Mr';
MERGE INTO people_target pt
USING (SELECT :person_id AS person_id,
:first_name AS first_name,
:last_name AS last_name,
:title AS title FROM DUAL) ps
ON (pt.person_id = ps.person_id)
WHEN MATCHED THEN UPDATE
SET pt.first_name = ps.first_name,
pt.last_name = ps.last_name,
pt.title = ps.title
WHEN NOT MATCHED THEN INSERT
(pt.person_id, pt.first_name, pt.last_name, pt.title)
VALUES (ps.person_id, ps.first_name, ps.last_name, ps.title);
次の文は、people_target
表の内容を表示し、ロールバックを実行します。
SELECT * FROM people_target;
PERSON_ID FIRST_NAME LAST_NAME TITLE
---------- -------------------- -------------------- ----------
1 John Smith Mr
2 alice jones Mrs
3 Gerald Walker Mr
ROLLBACK;
次の文は、people_target
内の既存の行を更新するためのバインド変数の使用を示しています。MERGE
文は、新しい行の挿入に使用した文と同じであることに注意してください。
var person_id NUMBER;
var first_name VARCHAR2(20);
var last_name VARCHAR2(20);
var title VARCHAR2(10);
exec :person_id := 2;
exec :first_name := 'Alice';
exec :last_name := 'Jones';
exec :title := 'Mrs';
MERGE INTO people_target pt
USING (SELECT :person_id AS person_id,
:first_name AS first_name,
:last_name AS last_name,
:title AS title FROM DUAL) ps
ON (pt.person_id = ps.person_id)
WHEN MATCHED THEN UPDATE
SET pt.first_name = ps.first_name,
pt.last_name = ps.last_name,
pt.title = ps.title
WHEN NOT MATCHED THEN INSERT
(pt.person_id, pt.first_name, pt.last_name, pt.title)
VALUES (ps.person_id, ps.first_name, ps.last_name, ps.title);
次の文は、people_target
表の内容を表示し、ロールバックを実行します。
SELECT * FROM people_target;
PERSON_ID FIRST_NAME LAST_NAME TITLE
---------- -------------------- -------------------- ----------
1 John Smith Mr
2 Alice Jones Mrs
ROLLBACK;