
はじめに
Django のデータベースを MySQL から PostgreSQL に変更したところ、データを新規追加する際に IntegrityError が発生してしまいました。
IntegrityError at /myapp/create/
duplicate key value violates unique constraint "myappdata_pkey"(重複したキー値は、"myappdata_pkey" のユニーク制約に違反します)
DETAIL: Key (id)=(1) already exists.
...
主キーの Id 値がダブっているようですが、自動採番されるので新規登録時に重複するようなことは本来ないはずと思っていました。最終的には MySQL と PostgreSQL の連番処理の違いによるものと判明して対処できましたので、以下記事にしました。
原因
まず、Django の migration 機能で生成される CREATE TABLE の SQL 文を確認してみました。
CREATE TABLE "myappdata" (
"id" serial NOT NULL PRIMARY KEY,
...
);
主キーとなる "id" カラムに serial が設定されています。PostgreSQL のマニュアルを調べたところ、この設定が PostgreSQL での自動連番の設定となることを確認しました。PostgreSQL の連番は「シーケンス」という仕組みを使って実現していて、新しくレコードを追加する場合は前回の「シーケンス」に 1 を加えて新しいシーケンスを作るという処理になります。
今回は、 DBeaver というツールを使用して元々運用していた MySQL から PostgreSQL にデータを流したのですが、この処理ではシーケンスが更新されなかったようです。
状況の確認
エラー中に "myappdata_pkey" が違反していると書かれていることから、"myappdata" テーブルのシーケンスを調べます。シーケンス名は "テーブル名_フィールド名_seq" となります。今回の例では、
テーブル名: appdata
フィールド名: id
ですので、シーケンス名は "appdata_id_seq" となります。
下記でシーケンス情報を取得できます。
1 |
SELECT * FROM myappdata_id_seq; |
ここで得られた last_value の値が現在のシーケンス値となり、新規レコード追加時には +1 した値が使われます。
今回は、この値が適切に更新されておらず、すでに使用されている id 値より小さかったため重複エラーになってしまっていました。
解決
PostgreSQL には、シーケンス操作関数が用意されています。 setval() 関数でシーケンスの値を id の最大値に書き換えます。
ポイント
1 |
SELECT setval('myappdata_id_seq', (SELECT MAX(id) FROM myappdata)); |
これで正常に新規追加を行うことができるようになります。
参考