Day4 前半のゴール
「PostgreSQLの“連番の正体”=シーケンスをイメージできるようになる」
今日は、主キーでほぼ必ず出てくる「連番」の話です。
MySQL の AUTO_INCREMENT に慣れていると、PostgreSQL の SERIAL / BIGSERIAL は「似てるけど中身が違うやつ」です。
前半のゴールはこうです。
PostgreSQLの SERIAL / BIGSERIAL が「ただの型名」ではなく、「整数+シーケンスのセット」だと理解できる。
MySQLの AUTO_INCREMENT と、PostgreSQLの「シーケンス」という仕組みの違いをざっくり説明できる。
「なぜPostgreSQLはわざわざシーケンスを分けているのか」を、なんとなく納得できる。
SQLite・MySQLの感覚を土台にしながら、「PostgreSQLらしい連番の考え方」に切り替えていきます。
まずはおさらい:MySQLのAUTO_INCREMENT
「カラムに“自動で増える”性質がくっついている世界」
MySQLでは、主キーをこう定義していました。
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL
);
SQLINSERTするときは、id を省略して書けます。
INSERT INTO users (name) VALUES ('Taro'), ('Jiro');
SQLすると、id に 1, 2, 3, … と自動で連番が振られます。
「idカラムにAUTO_INCREMENTという性質が付いている」というイメージでした。
このとき、連番の“元ネタ”はテーブルの中に隠れていて、
普段はあまり意識しません。
PostgreSQLは、この「連番の元ネタ」を、もっと表に出して扱います。
それが「シーケンス(sequence)」です。
PostgreSQLのSERIAL / BIGSERIALとは何か
「整数カラム+シーケンス+デフォルト値をまとめた“お手軽記法”」
PostgreSQLで、MySQLのAUTO_INCREMENTっぽい主キーを作るとき、よくこう書きます。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL
);
SQLあるいは、もっと大きな値まで扱いたいときは BIGSERIAL を使います。
CREATE TABLE big_table (
id BIGSERIAL PRIMARY KEY,
data TEXT NOT NULL
);
SQL見た目だけ見ると、「SERIALって型なのかな?」と思いがちですが、
実はこれは「便利なショートカット」です。
SERIAL は、内部的には次の3つをまとめてやってくれています。
整数型のカラムを作る(INTEGER)
専用のシーケンスオブジェクトを作る
そのカラムのデフォルト値に「そのシーケンスから次の値を取る」式を設定する
つまり、こう書いたのとほぼ同じ意味になります。
CREATE SEQUENCE users_id_seq;
CREATE TABLE users (
id INTEGER NOT NULL DEFAULT nextval('users_id_seq'),
name VARCHAR(50) NOT NULL
);
ALTER SEQUENCE users_id_seq OWNED BY users.id;
SQLこれを毎回書くのはさすがに面倒なので、
「SERIAL と書いたらここまで全部やってあげるよ」というのが、PostgreSQLの親切機能です。
ここでの重要ポイントは、
「PostgreSQLの連番は“シーケンス”という独立したオブジェクトが本体で、SERIALはその糖衣構文」
という理解です。
シーケンスとは何か
「“次の番号ちょうだい”と言うと、ひたすら増え続ける番号発生器」
シーケンス(sequence)は、ざっくり言うと「連番を発行する専用オブジェクト」です。
テーブルとは別に存在していて、nextval という関数で「次の番号」を取り出します。
例えば、さっきの users_id_seq があるとします。
psql からこう呼び出せます。
SELECT nextval('users_id_seq');
SQLこれを実行するたびに、1, 2, 3, 4, … と増えていきます。
テーブルにINSERTしなくても、シーケンス単体で番号を進めることができます。
SERIAL で定義したカラムは、この nextval('...') をデフォルト値として持っているので、
INSERT時にidを省略すると、自動的にシーケンスから番号が振られる、という仕組みです。
ここでのイメージはこうです。
MySQL:
「テーブルのカラムに“自動で増える”性質がくっついている」
PostgreSQL:
「テーブルとは別に“番号発生器(シーケンス)”がいて、カラムはそれをデフォルト値として使っている」
この分離が、後でじわじわ効いてきます。
SERIAL / BIGSERIAL と AUTO_INCREMENT の違いをざっくり整理
「“隠れたカウンタ”か、“独立した番号発生器”か」
MySQLのAUTO_INCREMENTと、PostgreSQLのSERIAL / シーケンスの違いを、感覚レベルでまとめるとこうなります。
MySQLのAUTO_INCREMENTは、
「テーブルの中にカウンタが埋め込まれていて、INSERTのたびにそれが増える」
というイメージです。
PostgreSQLのSERIALは、
「テーブルの外に“番号発生器(シーケンス)”がいて、INSERT時にそれを呼び出している」
というイメージです。
この違いが何を生むかというと、
シーケンスはテーブルとは独立しているので、
必要なら直接 nextval を呼んで番号を進めたり、currval で「さっき使った番号」を取ったりできる。
複数のテーブルで同じシーケンスを共有する、といった設計も理論上は可能。
もちろん、初心者のうちは「SERIALを使えば勝手にシーケンスも作ってくれる」くらいの理解で十分です。
ただ、「PostgreSQLは連番を“シーケンス”としてちゃんとオブジェクト化している」という感覚だけは、早めに持っておくと後が楽になります。
Day4 前半のまとめ
MySQLの AUTO_INCREMENT は「カラムに自動採番の性質がくっついている」イメージだが、PostgreSQLの SERIAL / BIGSERIAL は「整数カラム+専用シーケンス+nextval(...) をデフォルト値に設定」という3点セットをまとめて定義するための糖衣構文であり、本体はあくまで「連番を発行する独立オブジェクト=シーケンス」である。
シーケンスは SELECT nextval('seq_name'); のように単体で呼び出せる“番号発生器”で、テーブルとは別に存在するため、SERIAL カラムは「INSERT時にそのシーケンスから番号をもらっているだけ」という構造になっており、「MySQLは“隠れたカウンタ”、PostgreSQLは“独立した番号発生器を使う”」という違いをイメージできるようになることが Day4 前半の着地点になる。
