Open3
Postgresqlのserial型カラムのシーケンスを取得・設定したい
現象
Postgresqlのローカル環境構築で、serial型に対して値を指定して初期データロードした結果、シーケンス値が進まずに登録されてしまってInsert時にDuplicate Errorになってしまうという事案があった。
-- create
> CREATE TABLE animal (
id SERIAL,
name VARCHAR(10),
PRIMARY KEY(id)
);
-- initial load
> INSERT INTO animal (id, name) VALUES (1, 'cat');
> INSERT INTO animal (id, name) VALUES (2, 'dog');
-- insert (without id)
> INSERT INTO animal (name) VALUES ('tiger');
ERROR: duplicate key value violates unique constraint "animal_pkey"
DETAIL: Key (id)=(1) already exists.
この現象に対しては、当たり前だがinitial load時にはserial型に値を指定せず登録すれば問題は発生しなくなる。
が、そもそも現在のserial型のシーケンス値を確認・更新するにはどうしたら良いんだろう?ということで調べてみた。
serial型のシーケンス名取得: pg_get_serial_sequence
兎にも角にも、まずはシーケンス名を取得する必要がある。
そして結論から言うと pg_get_serial_sequence
関数を使えば良い。
フォーマットとしては以下のような感じ。
-- format
pg_get_serial_sequence([table_name], [column_name])
先の例だと、以下でシーケンス名を取得できる。
> SELECT pg_get_serial_sequence('animal', 'id');
pg_get_serial_sequence
------------------------
public.animal_id_seq
serial型のシーケンス値取得: currval
シーケンス名が分かれば後は通常のシーケンス操作をすれば良い。
取得に関しては currval
関数を利用する。
-- format
currval([sequence_name])
先の例だと以下のような感じ。
> SELECT currval('public.animal_id_seq');
currval
---------
1
serial型のシーケンス値更新: setval
更新も同様に、シーケンスを更新する関数 setval
を利用すれば良い。
-- format
setval([sequence_name], [value])
先の例だと以下のような感じ。
> SELECT setval('public.animal_id_seq', 10000);
setval
---------
10000
なお、その名の通り次のシーケンスに値をすすめる nextval
関数も存在する。
今回のケースを修正したい場合
上記をワンライナーで書けばいけそう。
たとえば、idの最大値にcurrvalを合わせたければ以下のようなSQLで良さそう。
idのMAXが 2
なので、2が設定されるはず。
> SELECT setval(pg_get_serial_sequence('animal', 'id'), (SELECT MAX(id) FROM animal));
setval
--------
2
当然だが、この後に先程のINSERT文を実行すると問題なく通る。
> INSERT INTO animal (name) VALUES ('tiger');
INSERT 0 1
> SELECT * FROM animal;
id | name
----+-------
1 | cat
2 | dog
3 | tiger
Ref
- Postgres13 - 9.26. システム情報関数と演算子: https://www.postgresql.jp/document/13/html/functions-info.html
- Postgres13 - 9.17. シーケンス操作関数: https://www.postgresql.jp/document/13/html/functions-sequence.html
「serial型のシーケンス値更新: setval」の書式説明の箇所、setval
と思しきところが serial
に成ってると思うのですが、私の勘違いでしょうか。
あああたしかに記述間違いでしたので修正いたしました。
ご指摘ありがとうございました!