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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?