テーブル設計で、正規化と非正規化をどう決めますか

基本は正規化して重複と食い違いを防ぎ、読み取りが多くて性能が足りないと測って分かった所だけ、集計の列やキャッシュで崩す、と答える問い。決済や履歴は消さずに変化を記録として残す、まで話します。

答えてみる

面接官に「テーブル設計で、正規化と非正規化をどう決めますか」と聞かれたつもりで、答えを見る前に自分の言葉で答えます。

声に出して答えた人は「書かずに答えを見る」で開けます。

解説を読む

聞かれ方から関連するコースまで、この問いの解説を全部ここに置いています。

こう聞かれます

  • 「テーブル設計で、正規化と非正規化をどう判断しますか」
  • 「一覧の画面が遅いので、集計した値を表に持たせたいと言われました。どうしますか」
  • 「注文の状態が変わったとき、行を上書きしますか」

面接官が見ている点

  • 正規化の用語の説明ではなく、ビジネスの要る物 (速さ、正しさ、あとで調べられること) と照らして決められるか
  • 崩すときに「測って遅いと分かったから」と根拠を言えるか
  • 崩したあとの食い違いを、どう防ぐかまで考えているか
  • お金や履歴のデータは消さずに残す、という感覚を持っているか

短い答え

  • 基本は正規化して、同じ事実を 1 か所にだけ置き、重複と食い違いを防ぎます。
  • 読み取りがとても多く、測って性能が足りないと分かった所だけ、集計の列やキャッシュで非正規化します。
  • 決済や履歴のデータは上書きや削除をせず、状態が変わるたびに記録として足していきます。

少し詳しい答え

まず正規化する理由

同じ値を何か所にも書くと、直すときに一部だけ古いまま残り、データが食い違います。食い違ったデータは、あとから正しい値を見つけるのが大変です。だから最初は正規化し、正しさを守ります。

崩すのは測ってから

一覧のたびに大きな表を集計して遅い、と分かったら、集計した値を別に持ちます。

SQL クエリ

-- 記事ごとのいいねの数を持つ
ALTER TABLE posts
  ADD COLUMN like_count
  INT NOT NULL DEFAULT 0;

いいねを足す処理と、この数を 1 増やす処理は、同じトランザクション (まとめて成功か失敗かにする単位) の中で行います。片方だけ成功すると、数がずれるからです。数がずれたときに、元の表から数え直す仕組みも用意しておきます。

集計の列のほかに、データベースの外にキャッシュ (よく読む結果の写し) を置く方法もあります。

決済や履歴は消さずに足す

注文の状態を 1 つの列で上書きすると、「いつ、誰が、何から何に変えたか」が消えます。お金に関わるデータは、問い合わせや監査で過去を確かめる必要があります。

SQL クエリ

-- 状態の変化を行として足す
CREATE TABLE order_events (
  id BIGINT PRIMARY KEY,
  order_id BIGINT NOT NULL,
  status TEXT NOT NULL,
  created_at TIMESTAMP
    NOT NULL
);

今の状態は最後の行を見れば分かり、過去のどの時点の状態もたどれます。

トレードオフ

非正規化すると読むのは速くなりますが、書く処理が増え、食い違いを防ぐ手間が一生ついて回ります。記録を足していく作りも、表が大きくなり、今の状態を出すのに一手間かかります。分析用の集計のように、少し古くてもよく、読むことがほとんどの用途では、最初から崩した形の表を別に作ることもあります。

よくある間違い

  • 遅くなりそうだから、と最初から崩す測る前に崩すと、食い違いのもとだけが増えます
  • 集計の列を足して、更新を別々に行う片方だけ成功すると数がずれます。同じトランザクションにまとめます
  • 注文の状態を上書きする過去の経緯が消え、問い合わせに答えられなくなります

深掘りの問い

  • 「集計の列と元のデータがずれたら」元の表を正として数え直す処理を用意し、定期的に照らし合わせる、と答えます
  • 「キャッシュと集計の列は、どう使い分けますか」 少し古くてよい読み取りはキャッシュ、正確さが要り画面ごとに使う値は集計の列、と答えます
  • 「消したいデータ (退会した人の情報) はどうしますか」個人の情報は消すか見えなくし、お金の記録は法令や会社の決まりで残す期間を確かめる、と答えます

確認日 2026年10月24日

技術面接問題の一覧へ戻る