---
title: "PostgreSQLのUPSERT実装｜ON CONFLICTの競合ターゲット指定とMERGE文の使い分け"
url: "https://www.issoh.co.jp/tech/details/16978/"
published: 2026-08-26
updated: 2026-08-29
categories: ["データベース"]
publisher: "株式会社一創"
---

# PostgreSQLのUPSERT実装｜ON CONFLICTの競合ターゲット指定とMERGE文の使い分け

UPSERTは「あれば更新、なければ挿入」と一行で説明できる処理ですが、実装で詰まるのは構文そのものではありません。競合ターゲットに何を書けば意図した制約が引かれるのか、同じコマンドに重複キーが混ざると何が起きるのか、複数セッションが同時に走ったときエラーで落ちるのはどちらか。この3点が分かれ目になります。

この記事では、`INSERT ... ON CONFLICT`と`MERGE`という2つの書き方について、公式ドキュメントの定義と版ごとの差分を突き合わせながら、選び方と実装手順を整理しました。データベース全般の位置づけは[データベースとは？種類・DBMS・RDBとNoSQLの選び方](https://www.issoh.co.jp/tech/details/13012/)、同時実行の前提となる分離レベルは[トランザクションとは？ACID・分離レベルとcommitの扱い](https://www.issoh.co.jp/tech/details/13690/)で扱っています。動作確認は18系（2026年8月時点の最新マイナーは2026年8月13日リリースの18.6）を前提としました。

## まとめ：UPSERTの実装で先に決める4点

結論は次の4点です。第一に、単一表への冪等な取り込みなら`INSERT ... ON CONFLICT DO UPDATE`を既定にします。別セッションが同時に挿入しても更新へ倒れるため、リトライ前提のAPIやバッチと相性が良い書き方です。

第二に、競合ターゲットの指定は`DO UPDATE`では必須、`DO NOTHING`では省略可という非対称があります。さらに競合判定に使える対象は`NOT DEFERRABLE`な制約と一意インデックスに限られるため、遅延可能な制約や除外制約を前提にした設計だと`DO UPDATE`が書けません。

第三に、`MERGE`は15で入った標準SQLの構文ですが、UPSERTの単なる代替ではありません。公式ドキュメントは同時実行時の挙動について「両者には様々な差異と制限があり、交換可能ではない」と明記しており、同時INSERTが起きる経路では一意制約違反で落ちる可能性が残ります。

第四に、バルク投入では`COPY`で一時表へ入れてから1回のUPSERTへ流し、投入前にキー重複を1行へ潰します。同一コマンド内に同じキーが2行あると必ずエラーになるためです。以下、根拠と書き方を順に見ていきます。

## INSERT ON CONFLICTの構文と競合ターゲットを指定する3つの書き方

PostgreSQLのUPSERTは9.5で入った`ON CONFLICT`句が基本形になります。挿入を試み、指定した一意制約に当たった行だけを更新へ振り替える構造で、失敗した挿入を握りつぶすのではなく、競合を検出した時点で更新へ切り替える点が特徴です。

```
CREATE TABLE stock (
  sku        text PRIMARY KEY,
  qty        integer NOT NULL,
  updated_at timestamptz NOT NULL DEFAULT now()
);

INSERT INTO stock (sku, qty)
VALUES ('A-100', 5)
ON CONFLICT (sku)
DO UPDATE SET qty = EXCLUDED.qty, updated_at = now();
```

この形が動く前提は、`sku`に一意インデックスが存在することです。主キーでも一意制約でも構いませんが、インデックスが無い列を競合ターゲットに書くとエラーになります。

### 競合ターゲットを列で書く場合とON CONSTRAINTで書く場合の差

競合ターゲットには2つの書き方があります。列名や式を並べる推論形式と、`ON CONSTRAINT`で制約名を直接指定する形式です。

```
-- 推論形式：列の組み合わせから一意インデックスを特定する
ON CONFLICT (tenant_id, sku) DO UPDATE SET qty = EXCLUDED.qty

-- 制約名の直接指定：どの制約を見るか一意に決まる
ON CONFLICT ON CONSTRAINT stock_tenant_sku_key DO UPDATE SET qty = EXCLUDED.qty
```

推論形式は移植性が高く、制約名を変えても壊れません。一方で同じ列を含むインデックスが複数あると意図とずれるため、そこを固定したいときは制約名の指定が確実です。テーブル定義が自社管理下なら推論形式、外部のスキーマへ書き込むなら制約名という切り分けが扱いやすいでしょう。

ここで見落としやすいのが必須かどうかの差です。公式ドキュメントは、`DO NOTHING`では競合ターゲットの指定は任意で、省略すると使用可能なすべての制約との競合が処理されると定めています。対して`DO UPDATE`では競合ターゲットの指定が必須です。どの行を更新するか決まらないため、省略が許されません。

競合判定に使える対象そのものにも制限があります。ドキュメントは「除外制約は`ON CONFLICT DO UPDATE`の競合判定には対応しない。いずれの場合も`NOT DEFERRABLE`な制約と一意インデックスのみが対応する」と記述しています。時間範囲の重なりを除外制約で防ぐ設計や、遅延可能な一意制約を前提にした設計では、この構文が使えません。

### 部分インデックスや式インデックスを競合判定に使うときの書き方

論理削除を持つテーブルでは、生きている行だけに一意性を課す部分インデックスがよく使われます。この場合、競合ターゲットに述語をそのまま書き添えます。

```
CREATE UNIQUE INDEX stock_sku_active_uk
  ON stock (sku) WHERE deleted_at IS NULL;

INSERT INTO stock (sku, qty)
VALUES ('A-100', 5)
ON CONFLICT (sku) WHERE deleted_at IS NULL
DO UPDATE SET qty = EXCLUDED.qty;
```

`WHERE`を省くと、その部分インデックスは候補として推論されず、一致する一意インデックスが無いというエラーになります。式インデックスを使っている場合も同様で、インデックス定義と同じ式を競合ターゲット側にそのまま写します。

なお2か所の`WHERE`は役割が別物です。`ON CONFLICT`直後はどのインデックスを競合判定に使うかの指定、`DO UPDATE SET ...`の後ろは競合した行を実際に更新するかの条件になります。

### EXCLUDED擬似表とDO UPDATEのWHERE句それぞれの役割分担

`DO UPDATE`の中では、既存行をテーブル名（または別名）で、挿入しようとした行を`excluded`という特別な名前で参照します。両方を突き合わせられるので、値が変わったときだけ更新する書き方ができます。

```
INSERT INTO stock AS t (sku, qty, updated_at)
VALUES ('A-100', 5, now())
ON CONFLICT (sku) DO UPDATE
  SET qty = EXCLUDED.qty, updated_at = EXCLUDED.updated_at
  WHERE t.qty IS DISTINCT FROM EXCLUDED.qty;
```

`IS DISTINCT FROM`を使うのは、どちらかがNULLでも比較が真偽値として成立するためです。等号だとNULL同士の比較がNULLになり、条件が成立しません。この一手間で、値が同じ行の無駄な更新を止められます。

ただし更新を止めても、行ロックまでは止まりません。ドキュメントは「式が真を返した行だけが更新されるが、アクションが取られる際にはすべての行がロックされる」と明記しています。差分が無い行を大量に流し込むと、更新は発生しないのにロック競合だけが積み上がる状態になり得ます。

もうひとつ、`RETURNING`が返すのは挿入または更新に成功した行だけです。`WHERE`条件を満たさずロックだけされた行や、`DO NOTHING`で競合した行は返りません。採番されたIDを必ず受け取りたい処理では、無変更となる更新を書いて行を返させるなどの回避が要ります。

## MERGE文とINSERT ON CONFLICTを使い分けるときの判断基準と版差

15で`MERGE`が入り、UPSERTの書き方は2系統になりました。ただし2つは同じ問題を別の書き方で解いているのではなく、想定する処理の形が違います。取り違えると同時実行の場面で想定外のエラーに遭います。

### 15で入ったMERGEが受け持つ処理の範囲と基本の書式を確認する

15のリリースノートは`MERGE`を「あるテーブルを別のテーブルに合わせて調整するコマンド」と説明し、`INSERT ... ON CONFLICT`と似ているがよりバッチ指向であると位置づけています。つまり主眼は1行の冪等な書き込みではなく、ソース側の集合に合わせて対象表を寄せる処理です。

```
MERGE INTO stock AS t
USING staging_stock AS s
ON t.sku = s.sku
WHEN MATCHED AND t.qty IS DISTINCT FROM s.qty THEN
  UPDATE SET qty = s.qty, updated_at = now()
WHEN NOT MATCHED THEN
  INSERT (sku, qty) VALUES (s.sku, s.qty)
WHEN NOT MATCHED BY SOURCE THEN
  DELETE;
```

この例のように、挿入と更新に加えて削除まで1文で書けるのが`MERGE`の持ち味です。マスタの洗い替えのように「ソースに無い行は消す」処理を含む場合、`ON CONFLICT`では別の`DELETE`文を足す必要があるため、記述量に差が出ます。

結合を使う構造上の注意もあります。ドキュメントは、対象行1件につき候補となる変更行を高々1件しか生まない結合にすべきだと述べ、そうでなければ挿入の繰り返しは一意性違反、更新や削除の繰り返しはカーディナリティ違反になると説明しています。重複除去は`MERGE`でも省けません。

### 17で追加されたRETURNINGとmerge\_action関数の使い方

前掲の`WHEN NOT MATCHED BY SOURCE`は15では書けません。17のリリースノートに「`MERGE`に`WHEN NOT MATCHED BY SOURCE`を追加」として載った機能で、同じ17で`RETURNING`句の対応と`merge_action()`関数、更新可能ビューに対する`MERGE`も入りました。

```
MERGE INTO stock AS t
USING staging_stock AS s
ON t.sku = s.sku
WHEN MATCHED THEN UPDATE SET qty = s.qty
WHEN NOT MATCHED THEN INSERT (sku, qty) VALUES (s.sku, s.qty)
RETURNING merge_action(), t.sku, t.qty;
```

`merge_action()`はその行を生んだ操作の種別を返すため、挿入と更新の件数を分けて記録できます。取り込みバッチの監査ログを残す要件があるなら、17以上を選ぶ理由になるでしょう。16以下が対象に残る案件では、この書き方を前提にできません。

版の切り分けを整理すると次のとおりです。なお`WHEN NOT MATCHED BY SOURCE`と、意味を明示する`BY TARGET`の付加は標準SQLの拡張であるとドキュメントに注記されており、他のDBMSへそのまま持ち出せる書き方ではありません。

| 機能                        | 使える版  | 備考                  |
| ------------------------- | ----- | ------------------- |
| INSERT ON CONFLICT        | 9.5以降 | 単一表の冪等な書き込み         |
| UNIQUE NULLS NOT DISTINCT | 15以降  | NULLを重複として扱う        |
| MERGE の基本形                | 15以降  | 挿入・更新・削除を1文で        |
| MERGE の RETURNING         | 17以降  | merge\_action で種別取得 |
| NOT MATCHED BY SOURCE     | 17以降  | 標準SQLに対する拡張         |

### 同時実行でMERGEが一意制約違反を返す条件と3通りの回避策

使い分けの決め手になるのが同時実行時の挙動です。ドキュメントは`MERGE`に通常のトランザクション分離規則が適用されると述べたうえで、「同時にINSERTが発生した場合にUPDATEを実行する能力を持つ代替文として`INSERT ... ON CONFLICT`の使用を検討したい場合もある。両者には様々な差異と制限があり、交換可能ではない」と続けています。

READ COMMITTEDでは、一致判定はコマンド開始時点のスナップショットに対して行われます。同じキーを別セッションが直前に挿入してコミットしていた場合、`MERGE`側からは未一致に見えて`INSERT`へ進み、書き込みで一意制約違反になるでしょう。`INSERT ... ON CONFLICT`はこの経路を内部で扱うため、更新へ倒れます。

回避策は3通りです。ひとつは、同時INSERTが起こり得る経路では`ON CONFLICT`へ寄せること。ふたつめは、`MERGE`を使うなら排他的なバッチ枠で流すか、対象表を明示的にロックして単独実行を保証すること。みっつめは、一意制約違反（SQLSTATE 23505）と直列化失敗（40001）を捕捉して文単位でリトライすることです。

リトライは`MERGE`に限らず、SERIALIZABLEで運用するシステム全般に要る備えでもあります。分離レベルごとの挙動差は[トランザクションとは？ACID・分離レベルとロールバックの扱い](https://www.issoh.co.jp/tech/details/13690/)で整理しました。

## 一意制約とNULL・同時実行で起きるUPSERTの失敗を切り分ける

UPSERTの不具合報告は「重複が消えない」「たまにエラーで落ちる」という形で上がってきます。原因は競合判定の前提が崩れている場合、コマンド内部で衝突している場合、セッション間で衝突している場合の3つに分かれ、対処も違います。

### UNIQUE制約のNULLが競合判定をすり抜ける条件と版ごとの対処

もっとも気づきにくいのがNULLの扱いです。一意制約は既定でNULL同士を異なる値として扱うため、キー列にNULLが入る設計だと同じ内容の行が何度でも入ります。`ON CONFLICT`も競合を検出できず、挿入され続けます。

15でこの既定を変える指定が入りました。リリースノートには「一意制約とインデックスがNULL値を異ならない値として扱えるようにした」とあり、`UNIQUE NULLS NOT DISTINCT`を付けて作り直すことで挙動を変えられます。

```
-- 15以降：NULLを重複とみなす一意制約
ALTER TABLE stock
  ADD CONSTRAINT stock_sku_lot_uk UNIQUE NULLS NOT DISTINCT (sku, lot_no);

-- 14以前：番兵値へ寄せた式インデックスで代替する
CREATE UNIQUE INDEX stock_sku_lot_uk
  ON stock (sku, COALESCE(lot_no, ''));
```

14以前の案件では下段の式インデックスが現実解ですが、競合ターゲットにも同じ式を書く点に注意してください。運用中のテーブルへ後から入れる場合、既存の重複行を先に解消しないとインデックス作成自体が失敗します。

### 同一コマンド内の重複行が返すカーディナリティ違反とその対処法

次に多いのが、1回の`INSERT`に同じキーの行が複数含まれるケースです。この場合はエラーで停止します。

```
ERROR:  ON CONFLICT DO UPDATE command cannot affect row a second time
HINT:  Ensure that no rows proposed for insertion within the same command
       have duplicate constrained values.
```

ドキュメントは`ON CONFLICT DO UPDATE`付きの`INSERT`を決定的な文と位置づけ、既存の1行に2回以上影響を与えることは許されず、その状況ではカーディナリティ違反エラーが送出されると定めています。仕様どおりの動作なので、リトライしても結果は変わりません。

対処は投入側で重複を潰すことです。どれを採るかを明示して1行に畳みます。

```
INSERT INTO stock AS t (sku, qty, updated_at)
SELECT DISTINCT ON (sku) sku, qty, updated_at
FROM staging_stock
ORDER BY sku, updated_at DESC
ON CONFLICT (sku) DO UPDATE
  SET qty = EXCLUDED.qty, updated_at = EXCLUDED.updated_at;
```

`DISTINCT ON`は`ORDER BY`の先頭をキーと一致させます。上の書き方なら、同じ`sku`のうち更新時刻が最新の1行だけが残ります。

### 複数行のUPSERTで発生するデッドロックを抑える3つの緩和策

セッション間の衝突として現れるのがデッドロックです。2つのバッチが同じキー集合を扱い、行に触れる順序が逆になると、互いの行ロックを待ち合って一方が`deadlock detected`（SQLSTATE 40P01）で落ちます。前述のとおり`DO UPDATE`は更新しない行にもロックを取るため、差分が小さくても発生し得ます。

緩和策は3つです。投入前にキー順で並べ替えて触れる順序を揃えること、バッチのサイズを小さくして1トランザクションの保持時間を縮めること、40P01を捕捉して文単位で再実行することです。

ただし並べ替えは確率を下げる運用上の工夫であって、行ロックの取得順が並び順と一致することが仕様として保証されているわけではありません。並べ替えたからリトライ実装は不要、という設計は避けたほうが安全でしょう。検証はDockerで同じ版を立てると再現しやすく、手順は[Docker PostgreSQL構築の手順](https://www.issoh.co.jp/tech/details/16976/)にまとめてあります。

## バルク投入でUPSERTを回すときの実装パターンと踏みやすい制約

日次の差分取り込みでは、数万行から数百万行を1回のジョブで反映します。1行ずつ`INSERT ... ON CONFLICT`を投げる実装は往復回数がそのまま時間になるため、投入経路の設計が効きます。Laravelから同じ処理を書く場合の記法と件数分割は[Laravel upsertの使い方を扱った記事](https://www.issoh.co.jp/tech/details/17133/)にまとめました。

### COPYで一時表へ入れてから一括でUPSERTへ流し込む手順

定番は、生データを一時表へ`COPY`で流し込み、そこから1文でUPSERTする2段構えです。`COPY`は行ごとの構文解析を省けるぶん投入が速くなります。

```
BEGIN;

CREATE TEMP TABLE staging_stock (
  sku text, qty integer, updated_at timestamptz
) ON COMMIT DROP;

COPY staging_stock (sku, qty, updated_at)
  FROM STDIN WITH (FORMAT csv);

INSERT INTO stock AS t (sku, qty, updated_at)
SELECT DISTINCT ON (sku) sku, qty, updated_at
FROM staging_stock
ORDER BY sku, updated_at DESC
ON CONFLICT (sku) DO UPDATE
  SET qty = EXCLUDED.qty, updated_at = EXCLUDED.updated_at
  WHERE t.updated_at < EXCLUDED.updated_at;

COMMIT;
```

一時表は`ON COMMIT DROP`を付けておくと後始末が要りません。最後の`WHERE`は、取り込みデータのほうが新しい行だけを更新する条件です。古いデータが後から届いても値が巻き戻りません。

一時表は接続ごとの一時スキーマに作られるため、通常の表と同じ条件では一覧に出ません。確認手段は[PostgreSQLのテーブル一覧を取得する方法](https://www.issoh.co.jp/tech/details/16966/)で整理しました。

### バルク取り込みで更新対象を絞る条件とリトライ設計の決めどころ

取り込みを止めないためには、どこまでを1トランザクションに含めるかを先に決めておきましょう。全件を1トランザクションで包むと1行の異常で全体が巻き戻り、逆に細かく分けすぎると再開位置の管理が煩雑になります。

実務では、キー範囲やファイル単位でチャンクに割り、チャンクごとにコミットする形が扱いやすい構成です。UPSERT自体が冪等なので、失敗したチャンクは丸ごと再投入すれば整合が取れます。この再実行の安全性が単純な`INSERT`との違いです。

件数の記録は、17以上なら`MERGE`の`RETURNING merge_action()`で挿入と更新を分けられます。16以下には`ON CONFLICT`側で両者を判別する公式な手段が無く、投入前後の件数差など外側での計測に頼ることになります。

マネージド環境でも構文は変わりませんが、対応する版の確認は要ります。AWS上での版の扱いは[Aurora PostgreSQLとは？対応バージョンと移行判断](https://www.issoh.co.jp/tech/details/16970/)で整理しました。取り込み基盤そのものを設計から任せたい場合は、[データ分析基盤構築・MLOps構築支援](https://www.issoh.co.jp/service/ai/data-platform/)で差分取り込みからジョブ運用まで対応しています。

### パーティション表へUPSERTするときに残る仕様上の制限事項

大量データを扱う表はパーティション化していることが多く、ここにも制限があります。ドキュメントは、パーティション表に対する`INSERT`の`ON CONFLICT DO UPDATE`句で、競合した行のパーティションキーを別のパーティションへ移動させる形に更新することは現時点で対応していないと注記しています。

日付でパーティションを切っている表に対し、UPSERTで日付列を書き換える設計は成立しません。キー列の更新が必要なら、削除と挿入に分けるか、そもそもパーティションキーを不変の値にする設計へ寄せます。

## 受託開発でUPSERTを採用してよい条件と見送るべき場面の線引き

ここまでの制約を踏まえ、実案件でどちらを選ぶかの線引きを示します。判断は「同時実行が起きる経路か」と「削除まで含む差分同期か」の2軸でほぼ決まります。

### ON CONFLICTを既定にしてよい4条件と制約設計の前提

`INSERT ... ON CONFLICT DO UPDATE`を第一候補にしてよいのは、次の条件が揃う場合です。書き込み対象が単一表であること、一意性が`NOT DEFERRABLE`な制約または一意インデックスで表現できること、更新内容が投入値の上書きか投入値との単純な演算で書けること、そして同じキーへの書き込みが同時に発生し得ること。

この4条件に当てはまるなら、Webhook受信・リトライ付きのAPI・日次の差分取り込みのいずれでも同じ構文で通せます。重複した送信の吸収も同じ仕組みで済み、アプリ側に排他制御を書かずに冪等性を担保できます。同じ吸収を例外ハンドラで書くとブロックごとに副トランザクションが生じるため、費用の差は[PL/pgSQLとは？関数とプロシージャの違いと例外処理・カーソル](https://www.issoh.co.jp/tech/details/16998/)と突き合わせてください。なお競合して更新へ倒れた分だけ古い版のタプルが積み上がるため、取り込み量の多い表では[PostgreSQLのVACUUM運用｜autovacuumのしきい値設計とXID周回・肥大の切り分け](https://www.issoh.co.jp/tech/details/16982/)で示したしきい値の個別設定を併せて検討してください。

前提として、キー設計を先に固める必要があります。後からキーを足す修正は既存重複の解消とインデックス再作成を伴い、稼働中のテーブルでは停止時間の相談になるためです。一意性をどの列の組で担保するかは設計段階で明文化しておきましょう。

### MERGEへ寄せてよい条件と採用を見送るべき3つの典型的な場面

`MERGE`を選ぶのは、削除を含む洗い替えを1文で書きたい場合、条件分岐が複数あって`WHEN MATCHED AND ...`で表現したい場合、そして実行が排他的なバッチ枠に収まっていて同時INSERTが起きない前提を運用で担保できる場合に限ります。加えて17以上であれば、`merge_action()`で処理件数の内訳を監査ログへ残せるでしょう。他のDBMSからの移行で既存の`MERGE`資産がある場合も、書き換え量を抑える理由になります。

逆に見送るべき場面もはっきりしています。第一に、オンライン処理と同じ表へ並行して書き込む経路です。一意制約違反で落ちる可能性が残るため`ON CONFLICT`に寄せます。第二に、一意性を除外制約でしか表現できない設計です。時間範囲の重なりを禁じるようなケースでは、どちらの構文も競合判定に使えず、明示的なロックが要ります。

第三に、更新後の値が既存行の状態に依存して複雑に決まる場合です。集計を伴う更新をUPSERTへ押し込むと、可読性も検証性も落ちます。取り込みと確定処理を分け、一時表から複数の文で段階的に確定させる構成のほうが、テストも障害切り分けも楽でしょう。

版の選択も判断材料です。17以上なら`MERGE`の適用範囲は広がり、15や16で止まるなら実質的に`ON CONFLICT`中心の設計になります。版の入手性と費用は[PostgreSQLのライセンスと価格](https://www.issoh.co.jp/column/details/16916/)で扱いました。

## よくある質問

### DO NOTHINGでRETURNINGが空になるのはなぜですか？

挿入または更新に成功した行だけが返るという定義のためです。`DO NOTHING`で競合した行は挿入も更新もされず、結果に含まれません。採番されたIDが要る場合は、無変更となる更新を書いて行を返させるか、挿入分と既存分を別のクエリで取得します。

### MERGEとINSERT ON CONFLICTはどちらが速いですか？

一律の優劣はありません。単一キーへの少量の書き込みは一意インデックスを直接引く`ON CONFLICT`が素直な計画になりやすく、大量の差分反映では`MERGE`の計画が有利な場合もあります。実データの分布で変わるため、`EXPLAIN (ANALYZE, BUFFERS)`で両方を計測して決めてください。

### 複合キーやNULLを含む列でUPSERTするにはどうしますか？

複合キーは競合ターゲットに列を並べて`ON CONFLICT (tenant_id, sku)`と書きます。キー列にNULLが入り得るなら、15以降は`UNIQUE NULLS NOT DISTINCT`で制約を作り直すのが素直です。14以前では`COALESCE`で番兵値へ寄せた式インデックスを使います。

### 挿入した件数と更新した件数を分けて取得できますか？

17以上で`MERGE`を使うなら`RETURNING merge_action()`で操作種別ごとに取得できます。`ON CONFLICT`側に相当する公式な手段は無いため、内部列に依存する判定に頼らず、投入前後の件数差などで計測します。

### ON CONFLICTで除外制約は競合判定に使えますか？

`DO UPDATE`の競合判定には使えません。ドキュメントは、使えるのは`NOT DEFERRABLE`な制約と一意インデックスのみと定めています。競合ターゲットを省略した`DO NOTHING`なら使用可能なすべての制約が対象になるため、挿入を捨てる用途には使えます。

## 関連記事

- [トランザクションとは？ACID・分離レベルとコミットの扱いを実装目線で解説](https://www.issoh.co.jp/tech/details/13690/)
- [PostgreSQLのテーブル一覧を取得する方法｜メタコマンドとカタログの使い分け](https://www.issoh.co.jp/tech/details/16966/)
- [Docker PostgreSQL構築の手順｜18で変わったボリューム位置とcompose定義](https://www.issoh.co.jp/tech/details/16976/)
- [Aurora PostgreSQLとは？対応バージョン・拡張機能とRDSからの移行判断](https://www.issoh.co.jp/tech/details/16970/)
- [データベースとは？種類・DBMS・RDBとNoSQLの選び方を実装目線で解説](https://www.issoh.co.jp/tech/details/13012/)

---

出典: [PostgreSQLのUPSERT実装｜ON CONFLICTの競合ターゲット指定とMERGE文の使い分け](<https://www.issoh.co.jp/tech/details/16978/>)（株式会社一創）
