PostgreSQL | SQLite+MySQL経験者向け、30日で習得するPostgreSQL:差分理解 - Day4 シーケンス

SQL PostgreSQL
スポンサーリンク

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
);
SQL

INSERTするときは、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 前半の着地点になる。

タイトルとURLをコピーしました