症状が四つ、名前が四つ
この連載はモバイル招待状サービスを例にERDを読んできた。ERDはデータベースの表と表どうしの関係を一枚に描いた設計図だ。01回で管理対象であるエンティティと列(カラム)を、02回で1対多の関係を、03回で多対多の関係と結合(ジョイン)を見た。
講義は最後に上級トピックを四つ、紹介だけして終わる。N+1問題、トランザクション、インデックス、マイグレーションだ。名前だけ聞くと開発者の領域に思えるが、講義の説明の仕方はコードではなく症状だ。何が起きたら何を疑え、という形である。
バイブコーダーにはこのやり方が合う。AIにコードを任せた人が直接見られるのは画面と動作だ。コードの中の原因は探せなくても、「一覧が遅い」「ポイントが入っていない」には気づける。その症状に名前を付けられれば、AIに何を確かめてもらうかが決まる。
下の表がこの記事全体の要約だ。左の列の症状に出会ったら、真ん中の列の名前を入れて、右の列のようにAIに確認を頼む。
| 症状 | 疑うもの | AIへの依頼 |
|---|---|---|
| 一覧画面がとても遅い | N+1問題、インデックス | この一覧にN+1問題がないか、インデックスが抜けていないか、両方確かめて |
| 検索がとても遅い | インデックス | この検索で使う列にインデックスが設定されているか確かめて |
| 登録はできたのに300ポイントが入っていない | トランザクション | 登録とポイント付与が一つのトランザクションにまとまっているか確かめて |
| 列を別の表に変えるなど、既存データを移す必要がある | マイグレーション | 実行する前に段階ごとの計画を先に見せて |
一覧が遅い — N+1問題、そしてインデックス

招待状の一覧画面を思い浮かべてみよう。招待状ごとに、どのテンプレートを使っているかをサムネイルとテンプレートのアドレス(URL)付きで見せる。ところが招待状の表にはテンプレートの情報がなく、テンプレートのid(template_id)だけがある。03回で見たとおり、テンプレートの表でそのidの行を探し、アドレスを引いてきて付ける。これが結合だ。
一覧画面はこのデータをAPIで受け取る。APIは画面がデータをくださいと呼ぶ窓口だ。きちんと作った一覧APIは招待状とテンプレートを結合して一度に取ってくる。N+1問題は結合せずに、招待状の一覧を一度取ってきた後、一行ごとにテンプレートをもう一度問い合わせることだ。一覧の問い合わせ一回(1)に、行数分(N)の問い合わせが付くのでN+1と呼ぶ。
招待状が数件しかないうちは差が見えず、行が増えるほど問い合わせ回数も一緒に増える。だから講義は症状で覚えろと言う。一覧APIを呼んだらとても遅い、というときに一度疑ってみろということだ。そのときAIに「この一覧APIにN+1問題がないか確かめて」と頼めばいい。
インデックスも症状が似ている。インデックスはデータベースのために目次をあらかじめ作っておくことだ。目次のない本で目当ての箇所を探すには最初からめくるしかないように、インデックスのない列で検索すると表全体をなめる。検索がとても遅くなったらインデックスを疑う。
講義が強調するのは、二つを一緒に疑えという点だ。一覧のようなAPIを呼んでとても遅いなら、N+1問題なのか、インデックスがきちんと設定されていないのか、両方を尋ねるべきだ。症状が同じなので、片方だけ確かめると原因を見逃しうる。
招待状20件の一覧を描くとき
N+1(遅いほう)
① 招待状20件を問い合わせる → 1回
② 招待状ごとにテンプレートを別に問い合わせる → 20回
合計21回。招待状が増えれば一緒に増える
結合(速いほう)
① 招待状とテンプレートを template_id で結合して一度に問い合わせる → 1回登録はできたのにポイントがない — トランザクション
講義の例は会員登録だ。新規登録者に300ポイントを付与する機能があるとしよう。データベースにはユーザーの表とポイントの表が別々にある。一回の登録で両方の表に記録ができなければならない。
実際に登録してみると、ユーザーは作成されたのに300ポイントは追加されていない、ということが起こりうる。一緒に起きるべきことが、表が分かれているという理由で片方だけ済んでしまうのだ。ポイントのない半端なアカウントが残る。
トランザクションは複数の動作を一つのまとまりとして処理することだ。まとめられた動作は一緒に成功するか、一緒に失敗する。銀行振込で、自分の口座からお金が引かれることと相手の口座にお金が入ることが別々に動いてはいけないのと同じだ。ポイント付与が失敗したら、登録もなかったことにする。
症状は「一緒に起きるべきことのうち一つだけが起きている」だ。確かめ方も講義の例のとおり、自分で登録してみてポイントが入ったかを見ればいい。入っていなければAIに「会員登録と300ポイント付与が一つのトランザクションにまとまっているか確かめて。どちらかが失敗したら両方取り消されるようにして」と頼む。
| 登録 | ポイント付与 | トランザクションがないと | トランザクションがあると |
|---|---|---|---|
| 成功 | 成功 | 正常 | 正常 |
| 成功 | 失敗 | ポイントのないユーザーだけが残る | 両方取り消される |
テンプレートを表に移すとき — マイグレーション

マイグレーションはデータベースのエンティティを追加したり修正したりすることだ。講義によれば、表を一つ新しく足す程度なら大きな問題はない。気をつけるべきは、すでにたまったデータを移さなければならない変更だ。
代表的なのが02回で見たテンプレートの格上げだ。最初は招待状の表のテンプレート列に1・2・3のどれかだけを入れると決めていた。決めておいた値だけを入れさせるこの方式をenumという。テンプレートを管理する必要が出てきて、テンプレートを別の表に切り出した。設計図では線を一本変えるだけだが、すでに作られた招待状があるなら話が違ってくる。
順番はこうだ。まずテンプレートの表を作り、1・2・3をそこに入れる。各行にその行を指すidができる。次に既存の招待状のテンプレート値1・2・3を新しいidに一つずつ対応させ、template_idに埋める。二つの表はすでに関係を結んでいるので、この対応が合っていないとエラーになる。最後に、もう要らなくなった古いテンプレート列を消す。
こうした大規模な変更をAIに任せて何も見ないと事故が非常に多く起きる、というのが講義の警告だ。データベースを誤って操作してデータがすっかり消えたという話は、たいていマイグレーションに気を配らずAIに任せたケースだという。だからこの種の作業はAIだけに任せず、毎回自分で一度確かめる習慣をつけるよう勧めている。
確かめるといってもコードを読む必要はない。実行前に段階ごとの計画を先に見せてもらい、移す前と後で招待状の件数が同じか、すべての招待状にtemplate_idが埋まっているかを確かめてから、古い列を消させる。消す段階がいちばん元に戻しにくいからだ。
前: invitations
id title template
1 招待状 A 1
2 招待状 B 3
① templates 表を作り 1・2・3 を入れる
id url thumbnail
t-01 ... ...
t-02 ... ...
t-03 ... ...
② invitations.template_id を埋める
template 1 → templates.id t-01
template 3 → templates.id t-03
③ 古い invitations.template 列を消す ← いちばん戻しにくいテンプレート列を templates 表に移すマイグレーションをしたい。
実行する前に段階ごとの計画を先に見せて。
移す前と後で招待状の件数が同じか、
すべての招待状に template_id が埋まっているかを確かめる方法も一緒に教えて。
古い template 列は自分で確認してから消す。完成したERDが見せてくれるもの
講師がいちばん大事だと挙げたのは、四つの事故ではなく完成したERDそのものだ。完成したERDを見れば、何を実装して何を実装していないかが目に見える。
追加機能を開発するとき、どこに何を足せばいいかが見え、今の関連関係でどの機能ができてどの機能ができないかを自分ですばやく把握できる。01回で述べた、数か月後に「自分は何を作ったのか」を復元する地図がこれだ。
四つの事故もその地図の上に居場所がある。N+1問題は表と表をつなぐ線に沿ってデータを取ってくる方法の問題で、インデックスは表の中でよく探す列に付く目次だ。トランザクションは一つの動作が触れる二つの表のあいだの問題で、マイグレーションは線を引き直す瞬間の問題だ。ERDを読めれば、症状が出たときにAIにどこを見てもらえばいいかも見えてくる。
ここまでがERD講義をもとにした四回だ。次回からはHTML講義に移り、相談申し込みフォームを例に、UIがなぜ正解のある問題なのかを見る。
