0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PostgreSQLでbigserialなのに一意性制約違反が発生した原因 ── 手動INSERTによるシーケンス不整合とその解消方法

0
Posted at

はじめに

PostgreSQLで bigserial を主キーに設定したテーブルに対し、

  • INSERT文で主キーを指定していない
  • にもかかわらず
  • 一意性制約違反(SQLSTATE 23505)が発生する

という事象に遭遇しました。

調査の結果、原因は
「過去に主キーを手動でINSERTしてしまったことによるシーケンスと実データの不整合」
でした。

この記事では、

  • なぜ bigserial でも重複が起きるのか
  • 手動INSERTがどのように影響するのか
  • 実際に行った対処方法

を整理してまとめます。


前提条件

  • DB:PostgreSQL
  • 主キー:bigserial
  • 通常のINSERT文では主キーを指定していない

テーブル例

CREATE TABLE sample_table (
    id bigserial PRIMARY KEY,
    file_name text,
    status integer,
    updated_by text,
    updated_at timestamp
);

問題のINSERT文

アプリケーション側で実行していたINSERT文は以下です。

INSERT INTO sample_table (
    interchange_time,
    file_name,
    status,
    updated_by,
    updated_at,
    input_id
)
VALUES (
    :interchange_time,
    :file_name,
    :status,
    :updated_by,
    :updated_at,
    :input_id
)
RETURNING id;
  • 主キー id明示的に指定していない
  • 採番されたIDを取得するために RETURNING id を使用

このSQL自体に問題はありません。

それでも以下のエラーが発生しました。

ERROR: duplicate key value violates unique constraint
SQLSTATE: 23505

原因は「過去の手動INSERT」

起きていたこと

過去に、次のような 主キーを明示指定したINSERT が行われていました。

INSERT INTO sample_table (id, file_name)
VALUES (100, 'sample.csv');

この操作自体はPostgreSQLでは可能ですが、ここに落とし穴があります。


なぜそれが問題になるのか?

bigserialの正体

bigserial は以下の組み合わせです。

  • bigint 型のカラム
  • 専用の シーケンス
  • nextval(シーケンス名) が DEFAULT に設定されている

🔴 重要なポイント

PostgreSQLのシーケンスは
「テーブルの実データ」を自動では見てくれない


具体例

  1. 過去に手動でこうINSERT

    INSERT INTO sample_table (id, file_name)
    VALUES (100, 'manual.csv');
    
  2. その時点でのシーケンス値は仮に 50

  3. 次に通常のINSERTを実行

    • シーケンスから 51 が採番される
  4. すでに同じIDが存在 → 23505エラー

つまり、

手動INSERTによって
テーブルの最大IDとシーケンスの値がズレたことが原因

でした。


対策:シーケンスを実データに合わせる

① テーブルの最大IDを確認

SELECT MAX(id) FROM sample_table;

例:結果が 100


② シーケンスをリセット

SELECT setval('sample_table_id_seq', 100, true);
  • 第2引数:現在の最大ID
  • 第3引数:true
    • 次のINSERTで 101 が採番される

※ シーケンス名は以下で確認できます。

\d sample_table

対応後の結果

  • シーケンス値と実データが整合
  • INSERT時の一意性制約違反は発生しなくなった
  • 問題は完全に解消

再発防止のための注意点

✅ 原則

  • bigserial のカラムは INSERT文で指定しない

✅ 例外的に指定する場合

  • 手動でIDを指定してINSERTしたら
  • 必ずシーケンスを進める

例:

SELECT setval(
  'sample_table_id_seq',
  (SELECT MAX(id) FROM sample_table),
  true
);

まとめ

  • bigserial でも 過去の手動INSERTで簡単に壊れる
  • INSERT文が正しくても、**原因は「昔のデータ操作」**ということがある
  • 23505エラーが出たら シーケンスと最大IDのズレを疑う
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?