- データベース
PostgreSQL連番|serialとIDENTITYの違いとsetval修正
PostgreSQLで連番を作るには、列を GENERATED ... AS IDENTITY(IDENTITY列)または serial 型にします。どちらも内部では「シーケンス」という採番専用のオブジェクトが番号を払い出します。新しく作るテーブルなら、標準SQLに沿っていて権限や型変更の扱いがシンプルな IDENTITY 列がおすすめです。
連番まわりで多い困りごとは、データ移行やCSV取り込みのあとに出る duplicate key value violates unique constraint です。この記事では、PostgreSQL 16.15でこのエラーを serial と IDENTITY の両方で再現し、setval で直す手順を実行結果つきで示します。あわせて、シーケンス関数の一覧、ロールバックやキャッシュで欠番が出る様子の実測、serial から IDENTITY への移行SQLまでまとめました。
PostgreSQL連番の早見表
| やりたいこと | コピペ用SQL |
|---|---|
| 連番の主キーを作る(おすすめ) | id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY |
| 値の手動指定も許す連番 | id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY |
| serialで作る | id serial PRIMARY KEY(大きくなるなら bigserial) |
| 開始値・増分を決める | GENERATED ALWAYS AS IDENTITY (START WITH 1000 INCREMENT BY 10) |
| 列に紐づくシーケンス名を調べる | SELECT pg_get_serial_sequence('members', 'id'); |
| 次の番号を取る | SELECT nextval('members_id_seq'); |
| 採番された番号を受け取る | INSERT INTO members(name) VALUES ('佐藤') RETURNING id; |
| ずれた連番を最大値に合わせる | SELECT setval(pg_get_serial_sequence('members','id'), (SELECT max(id) FROM members)); |
| 空テーブルでも安全に合わせる | SELECT setval(pg_get_serial_sequence('t','id'), COALESCE((SELECT max(id) FROM t), 0) + 1, false); |
| IDENTITY列の番号を指定値から再開 | ALTER TABLE tasks ALTER COLUMN id RESTART WITH 1000; |
| シーケンスを最初から | ALTER SEQUENCE demo_seq RESTART; |
| 全削除して番号も1から | TRUNCATE members RESTART IDENTITY; |
| ALWAYS列に値を入れる | INSERT INTO t(id, name) OVERRIDING SYSTEM VALUE VALUES (100, 'x'); |
| serialをIDENTITYに移行 | DEFAULT削除 → シーケンス削除 → ADD GENERATED BY DEFAULT AS IDENTITY → setval(後述) |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PostgreSQL連番|serialとIDENTITYの違いとsetval修正」
serial・IDENTITY・手動シーケンスの違い
serial は「integer列+シーケンス+DEFAULT nextval(...)」をまとめて作る省略記法で、IDENTITY は列そのものに採番の性質を持たせる標準SQLの書き方です。 5種類を同じ条件で作り、システムカタログで中身を確認しました。
CREATE TABLE t_serial (id serial PRIMARY KEY, name text);
CREATE TABLE t_bigserial (id bigserial PRIMARY KEY, name text);
CREATE TABLE t_always (id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name text);
CREATE TABLE t_bydefault (id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name text);
CREATE SEQUENCE manual_seq;
CREATE TABLE t_manual (id bigint PRIMARY KEY DEFAULT nextval('manual_seq'), name text);
SELECT c.relname AS table_name, format_type(a.atttypid, a.atttypmod) AS type,
a.attidentity AS identity, pg_get_expr(d.adbin, d.adrelid) AS default_expr,
pg_get_serial_sequence(c.relname, 'id') AS seq
FROM pg_attribute a JOIN pg_class c ON c.oid = a.attrelid
LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
WHERE c.relname IN ('t_serial','t_bigserial','t_always','t_bydefault','t_manual')
AND a.attname = 'id'
ORDER BY c.relname;
table_name | type | identity | default_expr | seq
-------------+---------+----------+-----------------------------------------+---------------------------
t_always | bigint | a | | public.t_always_id_seq
t_bigserial | bigint | | nextval('t_bigserial_id_seq'::regclass) | public.t_bigserial_id_seq
t_bydefault | bigint | d | | public.t_bydefault_id_seq
t_manual | bigint | | nextval('manual_seq'::regclass) |
t_serial | integer | | nextval('t_serial_id_seq'::regclass) | public.t_serial_id_seq
手動で作ったシーケンスは pg_get_serial_sequence で見つかりません(列に「所有」されていないため)。serial の上限は integer の 2,147,483,647、それ以外は bigint の 9,223,372,036,854,775,807 でした(pg_sequences.max_value で確認)。
比較表
| 項目 | serial | bigserial | IDENTITY ALWAYS | IDENTITY BY DEFAULT | 手動シーケンス |
|---|---|---|---|---|---|
| 書き方 | id serial |
id bigserial |
GENERATED ALWAYS AS IDENTITY |
GENERATED BY DEFAULT AS IDENTITY |
CREATE SEQUENCE + DEFAULT nextval() |
| 列の型 | integer | bigint | smallint/integer/bigint | smallint/integer/bigint | 任意の整数型 |
| 標準SQL | PostgreSQL独自 | PostgreSQL独自 | 標準 | 標準 | - |
| 値を指定したINSERT | 入る | 入る | エラー(OVERRIDING SYSTEM VALUE で可) |
入る | 入る |
| idのUPDATE | できる | できる | エラー(DEFAULTのみ可) | できる | できる |
| INSERTする人に必要な権限 | テーブル+シーケンスのUSAGE | 同左 | テーブルのINSERTだけ | テーブルのINSERTだけ | テーブル+シーケンスのUSAGE |
| 列の型をbigintに変えたとき | シーケンスはintegerのまま | - | シーケンスもbigintに追従 | 同左 | 別途変更 |
| テーブル削除時 | シーケンスも削除 | 同左 | 同左 | 同左 | 残る |
| 複数テーブルで共有 | 不可 | 不可 | 不可 | 不可 | 可 |
| 向いている用途 | 既存の資産 | 既存の資産 | 新規テーブルの主キー | 移行データを番号ごと入れる表 | 複数テーブルで通し番号 |
どれを選ぶか
| 状況 | 選ぶもの |
|---|---|
| 新しくテーブルを作る | bigint GENERATED ALWAYS AS IDENTITY |
| 既存データを番号ごと移行し、その後も手で番号を入れることがある | GENERATED BY DEFAULT AS IDENTITY |
| 既存テーブルがserialで動いている | そのままでよい。権限や型変更で手間がかかるなら移行を検討 |
| 注文番号と見積番号など、複数テーブルで通し番号にしたい | 手動シーケンス(DEFAULT nextval('共通seq')) |
| 番号を外部に見せる・推測されたくない | 連番ではなくUUIDなど(別記事で比較しています) |
IDENTITY列のALWAYSとBY DEFAULTの違い(実行結果)
INSERT INTO t_serial(id, name) VALUES (100, '明示'); -- serial
INSERT INTO t_bydefault(id, name) VALUES (100, '明示'); -- BY DEFAULT
INSERT INTO t_always(id, name) VALUES (100, '明示'); -- ALWAYS
INSERT 0 1
INSERT 0 1
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.
ALWAYS は、うっかり番号を手で入れてしまうことを防いでくれます。 移行などで意図して入れるときは、HINTのとおり OVERRIDING SYSTEM VALUE を付けます。
INSERT INTO t_always(id, name) OVERRIDING SYSTEM VALUE VALUES (100, '明示');
-- INSERT 0 1
INSERT INTO t_always(name) VALUES ('自動') RETURNING id;
id
----
1
100を入れたあとでも、自動採番は1から始まります。 シーケンスは手で入れた値を知らないためです。この性質が、次の章の duplicate key エラーにつながります。
UPDATEも同じように制限されます。
UPDATE t_always SET id = 5 WHERE id = 100;
ERROR: column "id" can only be updated to DEFAULT
DETAIL: Column "id" is an identity column defined as GENERATED ALWAYS.
UPDATE t_bydefault SET id = 5 WHERE id = 100;
UPDATE 1
逆に、BY DEFAULT 列で「指定された値を無視して採番したい」ときは OVERRIDING USER VALUE を使います。
INSERT INTO tasks(id, title) OVERRIDING USER VALUE VALUES (1, '指定値を無視') RETURNING id;
id
------
1001
duplicate keyエラーを再現してsetvalで直す
エラーが起きる流れ
番号付きのデータを INSERT や COPY で直接入れると、テーブルには番号が入りますが、シーケンスは進みません。 そのあと自動採番すると、シーケンスが1から払い出してしまい、既存の番号とぶつかります。
sequenceDiagram
participant M as 移行データ(id=1〜3)
participant T as membersテーブル
participant S as シーケンス(次は1)
M->>T: COPYでid=1,2,3を投入
Note over S: シーケンスは動かない
T->>S: 自動採番の INSERT
S-->>T: id=1 を払い出す
T-->>T: id=1 は既にある → duplicate key
serial と IDENTITY(BY DEFAULT)の両方で再現します。
CREATE TABLE members (id serial PRIMARY KEY, name text);
CREATE TABLE tasks (id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, title text);
-- members.csv
id,name
1,佐藤
2,鈴木
3,高橋
-- tasks.csv
id,title
1,資料作成
2,備品発注
t107=> \copy members(id,name) FROM 'members.csv' WITH (FORMAT csv, HEADER true)
COPY 3
t107=> \copy tasks(id,title) FROM 'tasks.csv' WITH (FORMAT csv, HEADER true)
COPY 2
t107=> INSERT INTO members(name) VALUES ('新規会員');
ERROR: duplicate key value violates unique constraint "members_pkey"
DETAIL: Key (id)=(1) already exists.
t107=> INSERT INTO tasks(title) VALUES ('新規タスク');
ERROR: duplicate key value violates unique constraint "tasks_pkey"
DETAIL: Key (id)=(1) already exists.
serial でも IDENTITY でも同じように起きます。 IDENTITY だから自動で合わせてくれる、ということはありません。
状態を確認する
t107=> SELECT max(id) AS max_id,
(SELECT last_value FROM members_id_seq) AS last_value,
(SELECT is_called FROM members_id_seq) AS is_called
FROM members;
max_id | last_value | is_called
--------+------------+-----------
3 | 1 | t
テーブルの最大値は3、シーケンスは1(失敗したINSERTで1を消費済み)。この「最大値>シーケンスの値」が原因です。 シーケンス名は pg_get_serial_sequence で調べられ、IDENTITY列でも使えます。
t107=> SELECT pg_get_serial_sequence('members','id') AS members_seq,
pg_get_serial_sequence('tasks','id') AS tasks_seq;
members_seq | tasks_seq
-----------------------+---------------------
public.members_id_seq | public.tasks_id_seq
setvalで直す
SELECT setval(pg_get_serial_sequence('members','id'), (SELECT max(id) FROM members));
SELECT setval(pg_get_serial_sequence('tasks','id'), (SELECT max(id) FROM tasks));
setval
--------
3
setval
--------
2
t107=> INSERT INTO members(name) VALUES ('新規会員') RETURNING id;
id
----
4
t107=> INSERT INTO tasks(title) VALUES ('新規タスク') RETURNING id;
id
----
3
シーケンスをテーブルの最大値に合わせると、次の採番は「最大値+1」になります。 IDENTITY 列でも、シーケンスを直接 setval してかまいません。
空のテーブルで setval するときの注意
| 書き方 | 空テーブルでの結果(実行) |
|---|---|
setval(seq, (SELECT max(id) FROM t)) |
NULLが返り、何も変わらない |
setval(seq, 0) |
ERROR: setval: value 0 is out of bounds for sequence "empty_t_id_seq" (1..2147483647) |
setval(seq, COALESCE((SELECT max(id) FROM t), 0) + 1, false) |
1が返り、次の採番は1(空でもデータありでも使える) |
t107=> SELECT setval(pg_get_serial_sequence('empty_t','id'), COALESCE((SELECT max(id) FROM empty_t), 0) + 1, false);
setval
--------
1
t107=> INSERT INTO empty_t DEFAULT VALUES RETURNING id;
id
----
1
データ移行のスクリプトには、空でも動くこの形を入れておくと安心です。
全テーブルのシーケンスをまとめて合わせる
移行後に、public スキーマで serial/IDENTITY を持つ全テーブルを一度に合わせるDOブロックです。
DO $$
DECLARE r record; v bigint;
BEGIN
FOR r IN
SELECT n.nspname, c.relname, a.attname,
pg_get_serial_sequence(format('%I.%I', n.nspname, c.relname), a.attname) AS seq
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum > 0 AND NOT a.attisdropped
WHERE c.relkind IN ('r','p') AND n.nspname = 'public'
AND pg_get_serial_sequence(format('%I.%I', n.nspname, c.relname), a.attname) IS NOT NULL
LOOP
EXECUTE format('SELECT COALESCE(max(%I), 0) FROM %I.%I', r.attname, r.nspname, r.relname) INTO v;
IF v > 0 THEN
PERFORM setval(r.seq, v, true); -- 次は v+1
ELSE
PERFORM setval(r.seq, 1, false); -- 空なら次は 1
END IF;
RAISE NOTICE '% -> %', r.seq, v;
END LOOP;
END $$;
実行結果(抜粋。わざと members と tasks のシーケンスを1に戻してから実行):
NOTICE: public.t_bigserial_id_seq -> 0
NOTICE: public.empty_t_id_seq -> 1
NOTICE: public.members_id_seq -> 12
NOTICE: public.tasks_id_seq -> 1002
NOTICE: public.articles_id_seq -> 4
DO
手動シーケンス(DEFAULT nextval('共通seq'))は対象外なので、個別に setval します。
シーケンス関数の一覧と実行結果
| 関数・構文 | 動き | 戻り値 |
|---|---|---|
nextval('seq') |
シーケンスを進めて次の値を取る | 新しい値 |
currval('seq') |
このセッションで最後に nextval した値 |
値(未実行ならエラー) |
lastval() |
このセッションで最後に nextval した値(シーケンスを問わない) |
値 |
setval('seq', n) |
現在値を n にする。次の nextval は n+1 | n |
setval('seq', n, false) |
現在値を n にし、次の nextval は n | n |
ALTER SEQUENCE seq RESTART |
開始値(START)に戻す | - |
ALTER SEQUENCE seq RESTART WITH n |
次の nextval を n にする | - |
ALTER TABLE t ALTER COLUMN id RESTART WITH n |
IDENTITY列の次の番号を n にする | - |
TRUNCATE t RESTART IDENTITY |
全行削除し、所有するシーケンスを開始値に戻す | - |
pg_get_serial_sequence('t','id') |
列が所有するシーケンス名を返す | public.t_id_seq |
CREATE SEQUENCE demo_seq;
SELECT currval('demo_seq');
SELECT nextval('demo_seq') AS n1, nextval('demo_seq') AS n2;
SELECT currval('demo_seq'), lastval();
SELECT setval('demo_seq', 10);
SELECT nextval('demo_seq');
SELECT setval('demo_seq', 10, false);
SELECT nextval('demo_seq');
ALTER SEQUENCE demo_seq RESTART;
SELECT nextval('demo_seq');
ALTER SEQUENCE demo_seq RESTART WITH 500;
SELECT nextval('demo_seq');
ERROR: currval of sequence "demo_seq" is not yet defined in this session
n1 | n2
----+----
1 | 2
currval | lastval
---------+---------
2 | 2
setval
--------
10
nextval
---------
11 ← setval(10) の次は 11
setval
--------
10
nextval
---------
10 ← setval(10, false) の次は 10
nextval
---------
1 ← RESTART で開始値へ
nextval
---------
500 ← RESTART WITH 500
setval の第3引数 is_called が、つまずきやすい点です。 true(省略時)は「n はもう使った」、false は「n はまだ使っていない」という意味になります。
IDENTITY列は列に対して RESTART できます。
t107=> ALTER TABLE tasks ALTER COLUMN id RESTART WITH 1000;
ALTER TABLE
t107=> INSERT INTO tasks(title) VALUES ('RESTART後') RETURNING id;
id
------
1000
採番された番号をアプリで受け取るときは、currval を別に呼ぶより INSERT ... RETURNING id が確実です。1回の往復で済み、どのシーケンスかを意識する必要もありません。
欠番はなぜ出るのか(ロールバック・エラー・キャッシュ)
シーケンスはトランザクションのロールバックで元に戻りません。 同時に多くのセッションが待たずに採番できるよう、そう設計されています。実際に試すと次のようになりました。
BEGIN;
INSERT INTO members(name) VALUES ('取り消す会員') RETURNING id; -- id = 5
ROLLBACK;
INSERT INTO members(name) VALUES ('次の会員') RETURNING id; -- id = 6(5は欠番)
INSERT INTO members(name) VALUES ('NG');
ERROR: new row for relation "members" violates check constraint "members_name_chk"
DETAIL: Failing row contains (9, NG). -- 9を消費
INSERT INTO members(name) VALUES ('OK') RETURNING id; -- id = 10
INSERT INTO members(name) VALUES ('OK') ON CONFLICT (name) DO NOTHING RETURNING id;
INSERT 0 0 -- 入らないが番号は消費
INSERT INTO members(name) VALUES ('OK2') RETURNING id; -- id = 12
| 欠番が出るケース | 実測 |
|---|---|
| ロールバック | 5が欠番 |
| 制約違反(CHECK、NOT NULL、主キー重複など)でINSERTが失敗 | 9が欠番 |
ON CONFLICT DO NOTHING で行が入らなかった |
11が欠番 |
| 行の削除 | 削除した番号はそのまま空く |
キャッシュ(CACHE n)を使い、セッションが終了 |
下の実測を参照 |
| サーバーのクラッシュ | 公式ドキュメントで欠番が出うると説明あり(未再現) |
複数セッションとキャッシュの実測
2つのセッション(A・B)から交互に nextval した結果です(Python + psycopg 3.3.6)。
import psycopg
DSN = "host=127.0.0.1 dbname=t107 user=t107_app password=t107pw"
with psycopg.connect(DSN, autocommit=True) as c:
c.execute("DROP SEQUENCE IF EXISTS s_nocache, s_cache20")
c.execute("CREATE SEQUENCE s_nocache") # CACHE 1(既定)
c.execute("CREATE SEQUENCE s_cache20 CACHE 20")
a = psycopg.connect(DSN, autocommit=True) # セッションA
b = psycopg.connect(DSN, autocommit=True) # セッションB
for seq in ["s_nocache", "s_cache20"]:
got = []
for who in ["A", "B", "A", "B", "A"]:
conn = a if who == "A" else b
got.append(f"{who}={conn.execute('SELECT nextval(%s)', [seq]).fetchone()[0]}")
print(f"{seq:10}", " ".join(got))
a.close(); b.close()
with psycopg.connect(DSN) as c: # 新しいセッション
print("再接続後 s_cache20:", c.execute("SELECT nextval('s_cache20')").fetchone()[0])
s_nocache A=1 B=2 A=3 B=4 A=5
s_cache20 A=1 B=21 A=2 B=22 A=3
再接続後 s_cache20: 41
CACHE 20 では、セッションごとに20個ずつ番号をまとめて確保します。 そのため番号は「挿入した順」にならず、セッションが終わると使い残した番号(4〜20、23〜40)は欠番になります。キャッシュは採番の負荷を下げますが、連番の順序や欠番なしを期待する用途には向きません。
欠番を出したくないときは
| 要件 | 方法 |
|---|---|
| 画面表示用に1から詰めた番号がほしい | 主キーとは別に、表示時に row_number() OVER (ORDER BY id) で付ける |
| 帳票の通し番号など、欠番なしが必須 | 採番用のテーブル(カウンタ)を SELECT ... FOR UPDATE で更新する。同時実行が直列になる点を考慮する |
| 並び順だけ保証したい | 作成日時の列を併用して並べる |
主キーの連番は「重ならない識別子」と割り切り、欠番があっても困らない設計にしておくと運用が楽になります。
serialからIDENTITYへ移行するSQL
既存の serial 列を IDENTITY に置き換える手順です。トランザクション内で実行し、最後に setval で番号を合わせます。
-- 移行前:id serial
CREATE TABLE articles (id serial PRIMARY KEY, title text);
INSERT INTO articles(title) VALUES ('記事A'), ('記事B'), ('記事C');
BEGIN;
ALTER TABLE articles ALTER COLUMN id DROP DEFAULT; -- nextval の DEFAULT を外す
DROP SEQUENCE articles_id_seq; -- 古いシーケンスを削除
ALTER TABLE articles ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY;
SELECT setval(pg_get_serial_sequence('articles','id'), (SELECT max(id) FROM articles));
COMMIT;
-- 移行前の \d articles
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+--------------------------------------
id | integer | | not null | nextval('articles_id_seq'::regclass)
-- 移行後の \d articles
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+----------------------------------
id | integer | | not null | generated by default as identity
t107=> INSERT INTO articles(title) VALUES ('記事D') RETURNING id;
id
----
4
新しいシーケンスは同じ名前(articles_id_seq)で作られ、続きの番号から採番されました。 続けて、ALWAYS への変更と bigint 化も行えます。
ALTER TABLE articles ALTER COLUMN id SET GENERATED ALWAYS;
ALTER TABLE articles ALTER COLUMN id TYPE bigint;
SELECT sequencename, data_type, last_value FROM pg_sequences WHERE sequencename = 'articles_id_seq';
sequencename | data_type | last_value
-----------------+-----------+------------
articles_id_seq | bigint | 4
IDENTITY 列は、列の型を bigint にするとシーケンスの型も bigint に揃います。 移行時の確認ポイントは次のとおりです。
| チェック | 内容 |
|---|---|
| 移行中の書き込み | ALTER TABLE はテーブルに強いロックを取るため、書き込みの少ない時間に行う |
| アプリが番号を指定してINSERTしていないか | ALWAYS にするなら、該当箇所に OVERRIDING SYSTEM VALUE が必要 |
| シーケンスへのGRANT | IDENTITY ではシーケンスのUSAGEは不要になる(残っていても動作に影響なし) |
| 移行後の採番 | INSERT ... RETURNING id が最大値+1になるか確認 |
serialで出やすい権限エラー
serial(と手動シーケンス)は、テーブルのINSERT権限に加えて、シーケンスのUSAGE権限が必要です。 INSERT と SELECT だけを付けたロールで試しました。
t107=> GRANT SELECT, INSERT ON members, tasks TO t107_writer;
-- t107_writer で実行
INSERT INTO members(name) VALUES ('権限テスト'); -- members は serial
ERROR: permission denied for sequence members_id_seq
INSERT INTO tasks(title) VALUES ('権限テスト') RETURNING id; -- tasks は IDENTITY
id
------
1002
serial のテーブルではシーケンスにも権限を付けます。
GRANT USAGE ON SEQUENCE members_id_seq TO t107_writer;
-- これから作るシーケンスにも自動で付ける場合
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE ON SEQUENCES TO t107_writer;
付与後は INSERT ... RETURNING id が 13 を返し、成功しました。IDENTITY 列ではこの追加の GRANT が不要なのも、新規テーブルで IDENTITY をすすめる理由の1つです。
よくある質問
Q. 欠番を詰め直して1から振り直してもよいですか?
A. 主キーは他のテーブルから外部キーで参照されていることが多く、振り直すと参照先も更新が必要になります。表示用の連番が目的なら、row_number() で付ける方法が手軽です。
Q. pg_dump でバックアップ・リストアしたあとも setval は必要ですか?
A. pg_dump の出力にはシーケンスの現在値を戻す setval が含まれるため、通常は不要です。CSVや別システムからデータを移したときに、この記事の手順で合わせます。
Q. シーケンスが上限に達するとどうなりますか?
A. nextval がエラーになり、INSERT できなくなります。serial(integer)の上限は約21億です。件数が多くなりそうなテーブルは、最初から bigint のIDENTITY(または bigserial)にしておくと安心です。
まとめ
- 新しいテーブルの連番は
bigint GENERATED ALWAYS AS IDENTITYがおすすめ - serial も IDENTITY も中身はシーケンス。 番号付きでINSERT/COPYするとシーケンスは進まない
- duplicate key は
setval(pg_get_serial_sequence('t','id'), max(id))で直る(serial・IDENTITY共通) - 空テーブルも考えるなら
COALESCE(max, 0) + 1, falseの形 setvalの第3引数 false は「次にその値を出す」、true(省略時)は「次はその値+1」- ロールバック・制約違反・ON CONFLICT・キャッシュで欠番は出る。 欠番なしを前提にしない
- serial はシーケンスのUSAGE権限が必要、IDENTITY は不要。 移行SQLも数行で書ける
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PostgreSQL連番|serialとIDENTITYの違いとsetval修正」
株式会社RJCでは、データベース設計の基礎を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。