PostgreSQLの「テーブル継承」、正直どう使えばいいの?現場の視点で解説するよ
やあ。最近、後輩から「PostgreSQLのテーブル継承って、結局いつ使うのが正解なんですか?」って聞かれたんだ。
たしかに、公式ドキュメントを読んでも「継承できるよ!」という仕様は書いてあるけど、実際にどういうシーンで活用すべきか、あるいは逆にどこで使うと地獄を見るのか……そのあたりの「現場の肌感覚」って意外と語られていないよね。
今日は、PostgreSQLのテーブル継承の勘所と、現代的な「宣言的パーティショニング」との使い分けについて、ちょっと深掘りしてみようか。
—
1. テーブル継承の「基本のキ」
PostgreSQLのテーブル継承は、オブジェクト指向プログラミングの「クラス継承」と似た感覚だと思えばいい。親テーブルを作って、それを継承した子テーブルを作る。
— 親テーブル
CREATE TABLE base_logs (
id serial PRIMARY KEY,
created_at timestamp DEFAULT now(),
message text
);
— 子テーブル
CREATE TABLE app_logs_2023 (
CHECK (created_at >= ‘2023-01-01’ AND created_at < '2024-01-01')
) INHERITS (base_logs);
こうすると、`SELECT FROM base_logs` を叩くだけで、子テーブル(`app_logs_2023`)の中身も一緒に引っ張ってこれる。これ、一見便利そうに見えるよね?共通カラムを一つの定義で管理できるから、DRY(Don't Repeat Yourself)の原則にも叶っているように感じる。
2. なぜ今、「継承」を安易に使ってはいけないのか
ここで一つ、現場からの警告だ。「単にカラムを共有したいから」という理由で継承を使うのは、一度立ち止まって考えてみてほしい。
継承テーブルには、いくつか特有の「落とし穴」があるんだ。
- インデックスと外部キーの制限: 親テーブルにインデックスを貼っても、自動的に子テーブルへ波及しない。個別に貼る必要があるんだ。また、親テーブルに対して外部キーを貼ることもできない(※これが一番痛い)。
- 整合性の維持が面倒: 「継承しているからデータ構造が同じ」と言っても、あくまでPostgreSQLの仕組み上は別々のテーブルだ。スキーマ変更時のALTER TABLEの挙動など、予期せぬトラブルに遭遇することがある。
昔、継承を使って複雑なデータモデルを組んだプロジェクトで、後から「外部キーが効かなくてデータ整合性が崩壊した」という修羅場を見たことがある。継承は強力だけど、データモデルの制約を緩めてしまうリスクも孕んでいるんだ。
3. 宣言的パーティショニングとの違い
じゃあ、パーティショニングはどうなの?という話になるよね。
PostgreSQL 10以降、「宣言的パーティショニング(Declarative Partitioning)」という機能が追加された。これが今の主流だ。
- テーブル継承: あくまで柔軟な「関係性」を作るための仕組み。子テーブルごとに全く違うカラムを持たせることも可能。
- 宣言的パーティショニング: 「巨大なデータを論理的に分割して、クエリを速くする」ための仕組み。構造は厳密に揃える必要がある。
もし君が、「ログデータを月ごとに分けたい」とか「古いデータをアーカイブしたい」という理由で継承を使おうとしているなら、迷わず宣言的パーティショニングを使いなさい。
— 宣言的パーティショニングの例
CREATE TABLE logs (
id serial,
created_at timestamp NOT NULL,
message text
) PARTITION BY RANGE (created_at);
CREATE TABLE logs_2023_01 PARTITION OF logs
FOR VALUES FROM (‘2023-01-01’) TO (‘2023-02-01’);
こっちはインデックスの継承も自動だし、クエリプランナーの最適化も段違いに効く。現代のPostgreSQLで「パーティショニング」を自作の継承でやるメリットは、正直ほとんどないと言っていい。
4. それでも「継承」が輝く瞬間
じゃあテーブル継承はもう不要なのか?というと、そうじゃない。
僕が唯一、テーブル継承を「アリだな」と思うのは、「メタデータや共通属性を持つ、完全に性質の異なるテーブル群を、横串で検索したい時」だ。
例えば、`products`という親テーブルを作り、その下に`books`、`electronics`、`clothing`という子テーブルを作るとする。それぞれのテーブルには固有のカラムがあるけれど、「検索機能」という共通のインターフェースで全商品を一括検索したい……そんなケースでは、継承は非常にエレガントな解になる。
まとめ:結局どうすればいい?
現場で設計する時の基準として、これだけ覚えておいて。
1. 単なるデータ分割(パフォーマンス目的)なら: 迷わず「宣言的パーティショニング」。これ一択。
2. テーブル構造をDRYにしたいだけなら: 継承は使わず、素直にカラムをコピーするか、必要ならJSONB型を使って柔軟性を持たせることを検討する。
3. 「ポリモーフィズム」的な構造が必要なら: そこで初めてテーブル継承を検討する。ただし、外部キー制約が使えないことの代替案(アプリ側でのバリデーションなど)をしっかり準備すること。
技術って、便利な道具ほど「どこで使うか」の判断が難しいよね。でも、PostgreSQLは奥が深い分、設計を正しく理解すれば本当に強力な武器になる。
何か具体的な設計で悩んだら、いつでもコードを持って相談に来てくれ。一緒に最高にクールなスキーマを考えようぜ!
コメント