Postgresを使用して開発をしている最中に下記のエラーに遭遇しました。
duplicate key value violates unique constraint \"author_pk\"
奇妙なのはPrimary keyであるAuthor.IDにはSERIALがちゃんと指定してあったことです。PostgresではSERIALを指定することでIDを自動で振り分けてくれます(Auto increment)。
CREATE table authors (
id SERIAL,
name text,
country text,
CONSTRAINT author_pk PRIMARY KEY(id)
);
なぜAuto incrementされてないのか
SERIALでのAuto incrementが効かなくなるシチュエーションはいろいろあると思いますが、今回でいうと別のSQLクエリで手動でInsertした後にSERIALの挙動がおかしくなっているようでした。 setvalで明示的に次に使用すべきSERIAL VALUEをセットしてみましたが、これだとマイグレーションが走るたびに強制的にNEXT SERIAL VALUEが3に戻るので、結局duplicate keyエラーが発生しました。
INSERT INTO authors (id, name, country)
values
(1, 'Kazuo Ishiguro', 'England'),
(2, 'Haruki Murakami', 'Japan')
ON CONFLICT do nothing;
SELECT setval('authors_id_seq', 3, true);
作成されたID | SERIALの値 | ||
---|---|---|---|
作成 | 3 | 3 | |
作成 | 4 | 4 | |
作成 | 5 | 5 | |
マイグレーション | 3 | ||
作成 | 3 | 3 | エラー! |
次のSERIALの値を動的に取得すべし
nextval() を使うことで次に使用されるSERIALの値を取得することができます。 その値を setval() で指定することで常に整合性のとれた値を設定することができます。
INSERT INTO authors (id, name, country)
values
(1, 'Kazuo Ishiguro', 'England'),
(2, 'Haruki Murakami', 'Japan')
ON CONFLICT do nothing;
SELECT setval('authors_id_seq', nextval('authors_id_seq'), false);
-- or
SELECT setval('authors_id_seq', nextval('authors_id_seq') - 1);