3秒でわかる
重複しない連番を発行するデータベースの機能。複数の接続から同時に呼ばれても値が重ならないため、主キーの自動採番によく使われます。
もう少し詳しく
どういうものか
シーケンスは、呼ばれるたびに次の数を返す独立したオブジェクトである。PostgreSQL や Oracle が備えており、nextval() を呼ぶと現在値を進めて返す。テーブルとは別に存在し、複数のテーブルから同じシーケンスを使うこともできる。
PostgreSQL では SERIAL や GENERATED AS IDENTITY を指定すると、裏でシーケンスが自動的に作られて既定値に結び付けられる。
なぜ必要か
主キーを自分で採番しようとすると、SELECT MAX(id) + 1 のような手順になる。これは同時に 2 つの接続が走ったときに同じ値を得てしまい、片方が一意制約で落ちる。
シーケンスは、この採番をトランザクションの外側で処理する。nextval() はロールバックしても値を戻さないため、待ち合わせが起きず、同時に呼ばれても必ず異なる値が返る。連番に欠番が出るのは、この設計と引き換えに得ている性質である。
具体例
-- 明示的に作る
CREATE SEQUENCE order_no_seq START WITH 1000 INCREMENT BY 1;
SELECT nextval('order_no_seq'); -- 1000
SELECT nextval('order_no_seq'); -- 1001
SELECT currval('order_no_seq'); -- 1001 (同一セッションの直近値)
-- テーブルの既定値として使う
CREATE TABLE orders (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
order_no BIGINT NOT NULL DEFAULT nextval('order_no_seq'),
total INT NOT NULL
);
INSERT INTO orders (total) VALUES (3200) RETURNING id, order_no;RETURNING を付けると、採番された値をその場で受け取れる。
つまずきやすいところ
既存データを INSERT 文で流し込むとき、id を明示して入れるとシーケンスは進まない。あとから自動採番の行を足した瞬間に、既存の id と衝突して一意制約に落ちる。データ移行のあとは setval() で現在値を最大 id に合わせておく。
SELECT setval('orders_id_seq', (SELECT MAX(id) FROM orders));もう 1 つ、シーケンスの値を業務上の連番として使うのは避けたい。エラーやロールバックで欠番が出るため、請求書番号のように欠番が問題になる用途には向かない。
似た用語との違い
MySQL には独立したシーケンスオブジェクトが無く、テーブルごとの AUTO_INCREMENT で同じ役割を担う。分散環境で採番したい場合は UUID や Snowflake ID のような、中央のカウンタを持たない方式が選ばれる。UUID は衝突しない代わりに値が大きく順序も持たないため、索引の効き方が変わる。