データベースを扱う現場で、「このデータがすでにテーブルに存在すれば更新(UPDATE)し、存在しなければ新しく挿入(INSERT)したい」という場面に遭遇したことはありませんか?例えば、日々の売上データを売上実績テーブルに反映する際、新しい店舗のデータは追加し、既存店舗のデータは最新の情報に更新するようなケースです。
このような処理を実装する場合、対象データの存在チェック(SELECT)を行い、その結果に応じて IF 文で UPDATE と INSERT を分岐させる方法が思い浮かぶかもしれません。しかし、この方法では SQL が複雑になりがちで、処理回数も増えてしまいます。
この「あれば更新、なければ挿入」という一般的な処理を、たった一つの SQL 文でスマートに実現できるのが MERGE 文(マージ)です。MERGE 文を使いこなせば、コードの可読性が向上し、パフォーマンス改善も期待できるため、データベーススペシャリスト試験の午後問題でも頻出の重要な構文です。
この記事では、MERGE 文の基本的な仕組みから、具体的な使い方、主要なデータベース製品ごとの構文の違いまで、図解や具体的なコード例を交えて体系的に解説していきます。
目次
MERGE文の基本概念│UPSERT処理でUPDATEとINSERTを一本化
MERGE 文を一言で表すなら、「条件に応じて UPDATE と INSERT を自動的に使い分ける賢い命令」です。この「あれば更新(UPDATE)、なければ挿入(INSERT)」という動作は、2つの単語を組み合わせて UPSERT(アプサート) とも呼ばれます。
UPSERT処理の身近な例:スマートフォンの連絡先同期
この動きをスマートフォンの連絡先で考えてみましょう。あなたが友人の新しい電話番号リスト(元データ)を手に入れ、自分のスマホの連絡帳(対象テーブル)を更新する状況を想像してください。
- すでに登録済みの友人(MATCHED): 古い情報なので、新しい電話番号に更新(UPDATE)します。
- まだ登録していない新しい友人(NOT MATCHED): 連絡帳にいないので、名前と電話番号を追加(INSERT)します。
この一連の作業を、一件ずつ「この人はもう登録されているかな?」と確認しながら手作業で行うのは大変です。MERGE 文は、この「確認 → 更新 or 挿入」という一連の判断と操作を、データベースシステムが自動で一括実行してくれる、非常に便利な機能なのです。
これにより、アプリケーション側で複雑な条件分岐のコードを書く必要がなくなり、データベースへの問い合わせも一度で済むため、処理のシンプルさとパフォーマンス向上が期待できます。
MERGE文の基本構文│USING句とON句による条件分岐の書き方
MERGE文の基本的な構文は、どのデータベース製品でもおおむね似ています。まずは、SQLの標準的な構文を見ていきましょう。これは「どのテーブルに(INTO)」「どのデータを使って(USING)」「どんな条件で(ON)」「一致したら(MATCHED)/しなかったら(NOT MATCHED)」を定義する構造になっています。
MERGE INTO target_table T -- 更新・挿入先の表 (対象テーブル)
USING source_table S -- 更新・挿入に使うデータ (元データ)
ON (T.id = S.id) -- 2つの表を紐づける条件
WHEN MATCHED THEN -- ONの条件が一致した場合 (データが存在した場合)
UPDATE SET T.column1 = S.column1, T.column2 = S.column2
WHEN NOT MATCHED THEN -- ONの条件が一致しなかった場合 (データが存在しなかった場合)
INSERT (id, column1, column2) VALUES (S.id, S.column1, S.column2);
各句の役割は以下の通りです。この構造を理解することが、MERGE文を読み解き、自分で書くための第一歩です。
句 | 役割 |
---|---|
`MERGE INTO <対象テーブル>` | 操作の対象となる、データを更新・挿入したいテーブルを指定します。 |
`USING <元データ>` | 登録したい新しい情報を持つテーブルやビュー、サブクエリなどを指定します。 |
`ON <結合条件>` | 「対象テーブル」と「元データ」のレコードが「同じもの」であるかを判断するための条件式です。主に主キーやユニークキーで比較します。 |
`WHEN MATCHED THEN ...` | `ON`句の条件に一致するレコードが「対象テーブル」に存在した場合の処理を記述します。通常は`UPDATE`文が続きます。 |
`WHEN NOT MATCHED THEN ...` | `ON`句の条件に一致するレコードが「対象テーブル」に存在しなかった場合の処理を記述します。通常は`INSERT`文が続きます。 |
この構文によって、「`USING`で指定した元データを、`ON`の条件で`INTO`で指定した対象テーブルと照合し、`MATCHED`なら`UPDATE`、`NOT MATCHED`なら`INSERT`を実行する」という一連の流れが定義されます。
実践的な使い方|商品マスタの更新をMERGE文で実行する具体例
概念や構文を理解したところで、実際の業務を想定した具体例を見ていきましょう。ここでは、「商品マスタ」テーブルに対して、日次で連携される「更新用データ」を使ってメンテナンスを行うシナリオを考えます。
シナリオ:
ECサイトの商品マスタ(`products`)を管理しています。毎日、別システムから変更があった商品の情報(`update_data`)が連携されます。このデータを使って、商品マスタを最新の状態に保ちます。
- 既存商品の情報が変更された場合: 価格や在庫数を更新したい (UPDATE)
- 新商品が追加された場合: マスタに新規登録したい (INSERT)
1. テーブルの状態(MERGE実行前)
まず、現在のテーブルの状態を確認します。
▼ `products`(対象テーブル)
product_id | product_name | price | stock |
---|---|---|---|
P001 | スーパーボール | 100 | 200 |
P002 | けしゴム | 80 | 500 |
P003 | えんぴつ | 60 | 800 |
▼ `update_data`(元データ)
product_id | product_name | price | stock |
---|---|---|---|
P002 | けしゴムNEO | 90 | 450 |
P004 | 定規 | 120 | 300 |
この元データを見ると、`P002`は`products`テーブルに存在するため`UPDATE`対象、`P004`は存在しないため`INSERT`対象となることがわかります。
2. MERGE文の実行
この処理を行う`MERGE`文は以下のようになります。
MERGE INTO products T
USING update_data S
ON (T.product_id = S.product_id)
WHEN MATCHED THEN
UPDATE SET
T.product_name = S.product_name,
T.price = S.price,
T.stock = S.stock
WHEN NOT MATCHED THEN
INSERT (product_id, product_name, price, stock)
VALUES (S.product_id, S.product_name, S.price, S.stock);
3. テーブルの状態(MERGE実行後)
上記のSQLを実行すると、`products`テーブルは以下のように更新されます。
▼ `products`(実行後の状態)
product_id | product_name | price | stock |
---|---|---|---|
P001 | スーパーボール | 100 | 200 |
P002 | けしゴムNEO | 90 | 450 |
P003 | えんぴつ | 60 | 800 |
P004 | 定規 | 120 | 300 |
期待通り、`P002`のレコードが更新され、`P004`のレコードが新規挿入されていることが確認できます。このように、`MERGE`文を使えば、複雑なデータメンテナンス処理を宣言的に記述できるのです。
主要DBMS(Oracle/SQL Server/PostgreSQL/MySQL)におけるUPSERT構文の違い
これまで解説してきた`MERGE`文はSQLの標準規格で定められていますが、製品によって対応状況や独自の構文が用意されています。データベーススペシャリスト試験の午後問題では、特定の製品名(例:Oracle)が指定されることもあるため、主要なDBMSでの書き方を把握しておくことが重要です。
Oracle, SQL Server:標準的なMERGE文
OracleとMicrosoft SQL Serverは、標準SQLに準拠した`MERGE`文をサポートしています。この記事で解説してきた構文がほぼそのまま利用可能です。ただし、SQL Serverでは文の最後にセミコロン(`;`)が必須であるなど、細かな方言(差異)は存在します。
-- Oracle, SQL Server で利用可能な標準的なMERGE文
MERGE INTO products T
USING update_data S ON (T.product_id = S.product_id)
WHEN MATCHED THEN
UPDATE SET T.price = S.price, T.stock = S.stock
WHEN NOT MATCHED THEN
INSERT (product_id, product_name, price, stock)
VALUES (S.product_id, S.product_name, S.price, S.stock);
PostgreSQL:`ON CONFLICT`句(UPSERT)
PostgreSQL(バージョン9.5以降)では、`MERGE`文とは異なる独自のUPSERT構文として `INSERT ... ON CONFLICT` が用意されています。これは「挿入(INSERT)を試みて、もし主キーやユニークキーの制約違反(CONFLICT)が起きたら、代わりに更新(UPDATE)する」という直感的な記述が可能です。
-- PostgreSQL のUPSERT構文
INSERT INTO products (product_id, product_name, price, stock)
VALUES ('P002', 'けしゴムNEO', 90, 450), ('P004', '定規', 120, 300)
ON CONFLICT (product_id) DO UPDATE SET
product_name = EXCLUDED.product_name,
price = EXCLUDED.price,
stock = EXCLUDED.stock;
- `ON CONFLICT (product_id)`: `product_id`カラムでキー重複が発生した場合のルールを定義します。
- `EXCLUDED`: 擬似テーブルで、本来挿入されるはずだった値(元データ)を参照します。
MySQL:`ON DUPLICATE KEY UPDATE`句
MySQLでも独自のUPSERT構文が使われます。`INSERT ... ON DUPLICATE KEY UPDATE` は、PostgreSQLと似た考え方で、「主キーまたはユニークインデックスで重複(DUPLICATE KEY)が発生した場合にUPDATE処理を行う」というものです。
-- MySQL のUPSERT構文
INSERT INTO products (product_id, product_name, price, stock)
VALUES ('P002', 'けしゴムNEO', 90, 450), ('P004', '定規', 120, 300)
ON DUPLICATE KEY UPDATE
product_name = VALUES(product_name),
price = VALUES(price),
stock = VALUES(stock);
- `VALUES(カラム名)`: `VALUES`句で指定された、挿入しようとした値を参照します。
このように、実現したいことは同じでもDBMSによって構文が大きく異なるため、自分が扱うシステムの書き方を正しく理解しておく必要があります。
MERGE文のメリットと注意点│DBスペシャリスト試験の論述対策
MERGE文は非常に強力ですが、万能ではありません。メリットを最大限に活かし、意図しないトラブルを避けるために、利用上の注意点も理解しておくことが、特にDBスペシャリスト試験の論述問題などでは重要になります。
MERGE文の主なメリット
- SQLのシンプル化と可読性向上: IF文による条件分岐や複数のSQL発行が不要になり、何をしている処理なのかが一目瞭然になります。これにより、コードのメンテナンス性が向上します。
- パフォーマンスの向上: ネットワークを介したDBとのやり取りが一度で済みます。アプリケーション側で「SELECTして存在確認 → INSERT or UPDATE」と2回以上の問い合わせを行うよりも、オーバーヘッドが少なくなり、処理が高速化される傾向があります。
- 原子性の確保(アトミック処理): 一連の「存在確認 → 挿入 or 更新」が単一の文として実行されるため、処理の途中で外部からデータ変更が割り込む余地がありません。これにより、データの整合性が担保されやすくなります。(これを「アトミックな操作」と言います)
利用上の注意点と対策
- 大量データの処理性能: MERGE文は対象テーブル(target_table)と元データ(source_table)の結合(JOIN)を内部的に行います。数億件といった大規模なテーブル同士のマージでは、結合コストがボトルネックとなり、想定以上にパフォーマンスが劣化する可能性があります。
- 対策: ON句で指定する列には必ずインデックスを設定する。また、処理を分割するなど、データ量に応じた設計を検討します。
- デッドロックのリスク: 高い頻度で複数のトランザクションが同じテーブルに対してMERGE文を実行すると、行ロックの競合が原因でデッドロックが発生する可能性があります。
- 対策: トランザクションの分離レベル(ISOLATION LEVEL)を適切に設定する、一度にマージするデータ量を減らす、といった検討が必要です。
- トリガーとの相性: MERGE文によってINSERTとUPDATEが実行されると、それぞれに設定されたトリガーが起動します。意図しないトリガーが連鎖的に動作しないか、事前に設計を確認することが重要です。
これらのメリット・デメリットを正しく理解し、適切な場面でMERGE文を活用する能力が、高度なデータベースエンジニアには求められます。