基準は一つ — 複数つくか
ERD は、サービスが何を管理し、それらが互いにどうつながるかを描いた図だ。Entity Relationship Diagram の略で、管理対象一つ(エンティティ)がデータベースでは Excel の表のようなテーブル一つになる。表の縦の欄一つがカラム、横の一行がデータ一件である。
講義はモバイル招待状サービスで説明する。管理者がログインして招待状を作る。ここでの管理対象は管理者と招待状の二つだ。招待状に入るタイトル・日付・会場・メッセージ・新郎の名前・新婦の名前は別に管理する対象ではなく招待状の属性なので、招待状テーブルのカラムになる。
サービスが順調なので機能を一つ足す。ゲストが「おめでとう」のようなお祝いメッセージを残す機能だ。メッセージは新しく書き、直し、消せなければならない。これを招待状テーブルのカラムとしてつけられるだろうか。
招待状一つにメッセージがちょうど一つだけなら、つけてもよい。わざわざテーブルを分ける必要はない。しかし結婚式一つには多くのゲストがメッセージを残す。一つの欄には値を一つしか入れられないので、このやり方は無理だ。そこでメッセージを別テーブルに分け、招待状と線でつなぐ。
講義が強調する感覚がこれだ。ある値が一つしか存在しないならカラム、複数生じうるならテーブル。機能を足すたびに、この値が対象一つにいくつつくかをまず問う。
[一つだけなら] invitations テーブルにカラム一つ
id title greeting
1 結婚します おめでとう
[複数なら] 別の greetings テーブル
id invitation_id content
1 1 おめでとう
2 1 お幸せに
3 1 ご結婚おめでとうございます1:N、親と子 — 線の端の記号の読み方
テーブル間の関係はほとんどが 1:N だ。一つに複数がつく関係という意味である。管理者一人が招待状を何枚も作り、招待状一枚にはメッセージがいくつもつく。どちらも 1:N だ。
一の側を親、複数の側を子と呼ぶ。招待状は自分を作った管理者一人だけを見ているので、管理者が親で招待状が子だ。子テーブルには親の id を書くカラムを置く。招待状テーブルの admin_id、上の例ではお祝いメッセージのテーブルの invitation_id がそうした欄だ。こうして別テーブルの行を指すカラムを外部キーと呼ぶ。
ERD では関係のある二つのテーブルの間に線を引き、線の端の形で数を表す。一の側は縦棒、複数の側は線の端が三つ又に開いた形だ。鳥の足に似ているので、カラスの足記法と呼ばれる。
必ず存在するかどうかも同じ場所に書く。必ずあるなら縦棒、ないこともあるなら丸だ。管理者がまだ招待状を一枚も作っていないこともあるので、招待状側の端には丸と三つ又が重なる。逆に招待状は管理者なしには生まれないので、親側の端はふつう縦棒二本になる。
線の端の二つの記号のうち、テーブルに近いほうが最大の数、テーブルから遠いほう(線の中ほど)が最小の数を表す。AI が描いた ERD でこの記号を読むだけで、サービスが何を許しているかが見える。
| 記号 | 位置 | 意味 |
|---|---|---|
| 縦棒 | | テーブルに近い側 | 最大一つ |
| 三つ又 { | テーブルに近い側 | 複数 |
| 縦棒 | | テーブルから遠い側 | 少なくとも一つ(必須) |
| 丸 o | テーブルから遠い側 | なくてもよい(任意) |
管理者 ||--o{ 招待状 管理者一人に招待状 0 枚以上
招待状 ||--o{ メッセージ 招待状一枚にメッセージ 0 件以上
招待状 ||--o| ライブ配信 招待状一枚にリンク 0 件または 1 件一つなのに分ける場合 — 任意の 1:1
次はライブ配信機能だ。式に直接来られないゲストのために式を生中継する有料サービスなので、申し込む人もいれば申し込まない人もいる。結婚式一つにライブリンクはないか一つかだ。複数ある必要はない。
こうした関係を 1:1、そのうちでもないことがあるので任意の 1:1 という。ライブ配信を別テーブルに分けるなら、招待状側の端は縦棒二本、ライブ配信側の端は丸と縦棒で描く。
ところが講義は、わざわざそうする必要はないと言う。招待状テーブルにリンクのカラムを一つ足し、申し込んだ人だけ埋めればよい。一つしか存在しない値なので、先の基準どおりカラムだ。
分けるのは、その欄が空いていることがあまりに多いときだ。申込者が十組に一組なら、残りの九行はその欄が空になる。講義は、こうして空欄が多く、より効率よく管理したいときに 1:1 で分けることもあると説明する。ただし主流の使い方ではない。1:1 は選択肢として覚えておけばよく、基本はカラムだ。
[基本] invitations テーブルにカラム一つ
id title live_stream_url
1 結婚します (空)
2 春の約束 https://live.example.com/abc
[任意] 別の live_streams テーブル
id invitation_id url
1 2 https://live.example.com/abcenum だったテンプレートがテーブルになる瞬間
最初の設計では、テンプレートは 1・2・3 の三種類に決めていた。こうして決めておいた値だけを受け付け、それ以外の値が入るとエラーにするデータ型を enum という。ログイン手段をカカオ・Google・Apple に限るのと同じやり方だ。
ところがテンプレートのデザインが物足りず、新しいデザイナーを採用してテンプレートをどんどん増やしたくなったとしよう。はじめはコードを直して 4 番を足し、5 番を足し、1 番を外す。こういうことが度々起きると、そのたびにコードやデータベースに手を入れるのが面倒になる。
このとき、テンプレートをいっそテーブルにする。テンプレートごとに自分の id を持ち(重複しないように作る ID 用の文字列である UUID を使う)、招待状は enum のカラムの代わりに template_id カラムを持つ。template_id はテンプレートテーブルの id を指す。テンプレート一つで招待状を何枚も作れるので、テンプレートと招待状は 1:N で、テンプレートが親だ。
講義が残す示唆はこれだ。いくつかの値に決めておいたものでも、あとで管理する必要が出てくればカラムではなくテーブルに分けられる。追加や削除が頻繁になったなら、その値は属性ではなく管理対象になったということだ。ただし、すでに溜まった招待状の 1・2・3 の値を新しい id へ移す作業には注意が要り、その話は 04 回で扱う。
今日やってみることは、AI に今のプロジェクトのテーブル構成を整理してもらい、下の依頼文で見直すことだ。次回は、招待状一つを複数の管理者で一緒に管理する多対多の関係を中間テーブルで解く方法と、コードとして書いた文字を図に変えてくれる Mermaid で AI に ERD を描いてもらう方法を見る。
[前] invitations — template が enum
id title template
1 結婚します template_1
2 春の約束 template_2
[後] templates テーブル + template_id
templates
id name
t-01 クラシック
t-02 フラワー
invitations
id title template_id
1 結婚します t-01
2 春の約束 t-02このプロジェクトのデータベースのテーブルとカラムを表に整理して。
そのうえで、次の三つを探して。
1. 複数入りうる値を一つのカラムに詰め込んでいる箇所
(例: greeting1、greeting2 のような番号つきカラム、カンマでつないだ値)
2. ほとんどの行で空になっているカラム
3. 決めておいた値(enum)のうち、今後管理者が追加・削除する必要があるもの
それぞれテーブルに分けるべきか、カラムのままでよいかを理由とともに教えて。
まだ何も変更しないで。