PostgreSQL 10 min read

PostgreSQLの「duplicate key value violates unique constraint」 ― setvalとON CONFLICT

※この記事にはアフィリエイト広告(プロモーション)が含まれます。

主キーや一意制約の列に既存と同じ値を入れると、PostgreSQL は次のエラーを返します。

ERROR:  duplicate key value violates unique constraint "items_code_key"
DETAIL:  Key (code)=(A) already exists.

この記事のログは PostgreSQL 18.6(Ubuntu 26.04 の標準パッケージ)で採りました。表は次のとおりです。

CREATE TABLE items (
  id   serial PRIMARY KEY,
  code text NOT NULL UNIQUE,
  name text
);

まず見る(結論)

  • 引用符の中が制約名、DETAIL の Key (列)=(値) がぶつかった列と値
  • 制約名が 表名_pkey で、値を指定していない id がぶつかっている → シーケンスが既存の最大値より遅れている。setval で合わせる
  • 重複したら何もしない・更新する → INSERT ... ON CONFLICT
  • トランザクションの中で起きると、後続の SQL が全部失敗する

制約名から列を引く

制約名を付けずに作ると、主キーは 表名_pkey、一意制約は 表名_列名_key になります。定義は pg_constraint で確かめられます。

SELECT conname, contype, pg_get_constraintdef(oid)
  FROM pg_constraint WHERE conrelid = 'items'::regclass;
       conname       | contype | pg_get_constraintdef
---------------------+---------+----------------------
 items_id_not_null   | n       | NOT NULL id
 items_code_not_null | n       | NOT NULL code
 items_pkey          | p       | PRIMARY KEY (id)
 items_code_key      | u       | UNIQUE (code)

PostgreSQL 18 では NOT NULL も contype = 'n' の制約として並びます。

id を手で入れるとシーケンスがずれる

データ移行などで id を指定して入れると、シーケンスは進みません。

INSERT INTO items (id, code, name) VALUES (1,'A','a'), (2,'B','b'), (3,'C','c');
SELECT last_value, is_called FROM items_id_seq;
 last_value | is_called
------------+-----------
          1 | f

この後で id を指定せずに入れると、シーケンスが 1 から値を出すので既存の id とぶつかります。

INSERT INTO items (code, name) VALUES ('D','d');
ERROR:  duplicate key value violates unique constraint "items_pkey"
DETAIL:  Key (id)=(1) already exists.

同じ INSERT を繰り返すと、Key (id)=(2)、(3) と失敗し、4回目に id=4 で入りました。失敗した INSERT でもシーケンスの値は消費されます。

setval で合わせる

シーケンスを表の最大値に合わせます。シーケンス名は pg_get_serial_sequence で引けます。

SELECT setval(pg_get_serial_sequence('items', 'id'), (SELECT max(id) FROM items));

id=1〜3 を入れた直後にこれを実行すると、次の INSERT は id=4 で入りました。

表が空だと max(id) が NULL になり、合わせられません。空の表で 1 から始めたいときは、第3引数に false を渡します。

SELECT setval(pg_get_serial_sequence('items', 'id'), 1, false);

identity 列も同じ

GENERATED ALWAYS AS IDENTITY の列は、値を指定すると拒否されます。

ERROR:  cannot insert a non-DEFAULT value into column "id"
DETAIL:  Column "id" is an identity column defined as GENERATED ALWAYS.
HINT:  Use OVERRIDING SYSTEM VALUE to override.

OVERRIDING SYSTEM VALUE を付けて id=1 を入れた後は、serial と同じく次の INSERT が Key (id)=(1) で失敗しました。setval(pg_get_serial_sequence(...)) で同じように直ります。

ON CONFLICT

重複したときの扱いを INSERT に書けます。ぶつかる列(または制約名)を指定します。

-- 何もしない(RETURNING は0行)
INSERT INTO items (code, name) VALUES ('A','a3')
  ON CONFLICT (code) DO NOTHING RETURNING id;

-- 既存の行を更新する。EXCLUDED は入れようとした行
INSERT INTO items (code, name) VALUES ('A','a4')
  ON CONFLICT (code) DO UPDATE SET name = EXCLUDED.name RETURNING id, name;
 id | name
----+------
  1 | a4

-- 制約名で指定する
INSERT INTO items (code, name) VALUES ('A','a5')
  ON CONFLICT ON CONSTRAINT items_code_key DO UPDATE SET name = EXCLUDED.name;

一意制約の無い列を指定するとエラーになります。

INSERT INTO items (code, name) VALUES ('A','a6') ON CONFLICT (name) DO NOTHING;
ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification

ON CONFLICT で行が入らなかった場合も、シーケンスは進みます。上の3回の ON CONFLICT の前はシーケンスが 11 で、次に新しい行を入れたときの id は 15 でした。

トランザクションの中で失敗すると後続も失敗する

BEGIN;
INSERT INTO items (code) VALUES ('G');
INSERT INTO items (code) VALUES ('A');   -- 重複
ERROR:  duplicate key value violates unique constraint "items_code_key"
INSERT INTO items (code) VALUES ('H');
ERROR:  current transaction is aborted, commands ignored until end of transaction block
COMMIT;
ROLLBACK

COMMIT は ROLLBACK として扱われ、失敗する前に入れた G も残りませんでした。失敗しうる INSERT の前に SAVEPOINT を置き、失敗したら ROLLBACK TO SAVEPOINT で戻します。こうすると G と H の両方がコミットされました。

BEGIN;
INSERT INTO items (code) VALUES ('G');
SAVEPOINT s1;
INSERT INTO items (code) VALUES ('A');   -- 重複
ROLLBACK TO s1;
INSERT INTO items (code) VALUES ('H');
COMMIT;

PostgreSQL をもっと深く知るための本

PostgreSQL実践入門 ──アーキテクチャ、運用監視、性能改善

技術評論社・2026年2月

アーキテクチャから運用監視・性能改善までを扱う。付録に PostgreSQL 18 の新機能と設定項目の差分がある。

Amazonで見る

[改訂3版]内部構造から学ぶPostgreSQL ―設計・運用計画の鉄則

技術評論社・2022年11月・PostgreSQL 14 ベース

内部構造の理解から、設計と運用計画の立て方を学ぶ。

Amazonで見る

Amazonのアソシエイトとして、FIRE SE は適格販売により収入を得ています。