「データベース設計の正規化って難しい」「どこまでやったらできたと言えるんだろう」
こんなふうに思ったことはないだろうか。
データベース設計において正規化は、マスター必須の領域だ。しかし、初心者には理解が難しいことも事実だ。
実際、筆者もエンジニアになって2年目まではあまり得意ではなかった。
そもそも、単語が難しすぎてとっつきにくい。第一正規形や関数従属性、推移的関数中属性といった難しい単語に理解を阻まれてしまう。
また、そもそもデータベース設計をしっかりと業務でやる機会も少ないため、自分でやっていることが正しいのかを確認する機会もあまりなかった。
しかし、安心して欲しい。この記事は、上記のような過去の自分と同じ悩みを持っている方が、効率よく自信を持ってエンジニアスキルを伸ばして欲しいという思いから執筆している。
そのため、正規化について手順を以下の3つに沿ってわかりやすく解説する。
ステップ1:同一行での繰り返しをなくす(第1正規形)
ステップ2:主キーの一部への依存関係をなくす(第2正規形)
ステップ3:主キーではない項目への依存関係をなくす(第3正規形)
また、一歩進んだ知識をつけてもらうために正規化をあえてしない場合もあることを事例とともに伝える。
それでは実際に見ていこう。
1 ステップ1:同一行での繰り返しをなくす(第1正規形)
正規化の最初のステップとして、同一行での繰り返しをなくす作業をする。この結果出来上がるのが、いわゆる第1正規形だ。
修正前 | 修正後 | |
---|---|---|
パターン1(似たカラムが存在している) | ||
パターン2(一つのカラムに複数の値が入っている) |
1−1 第1正規形のポイント
第1正規形にするためには、一つのレコード(行)に重複したデータがない状態を作ることだ。そのため、もし重複データがあった場合は、行を分ける対応をする。
非正規形
重複データがある状態を非正規形という。この状態には以下の2パターンがある。
1 同じカラムが複数回出てくる。
2 一つのカラムに複数の値が入っている(カンマ区切りなど)
以下の画像が1番目の例だ。「コースID」「コース名」「教授名」カラムが2回出てきている。
以下の画像が2番目の例だ。「所属クラブ」「クラブ指導者」カラムに値が2つ入っている。
第1正規形
非正規形のテーブルを第一正規形にすると以下のようになる。「田中」さんは二つのクラブに入っているが、それをまとめて1行で表現してしまっていた。これを2行で表現する。
1−2 第1正規形じゃないと困ること
第1正規形になっていない場合「拡張性がない」という課題がある。
これは特に非正規形の1番目の例で示したような場合に当てはまる。この場合、対象項目の数が増えるとテーブルのカラムが増える。つまり、テーブル定義の変更が必要になる。
例えば、以下のテーブル定義だとクラブは2つまでしか所属できない。3つ以上のクラブに加入できるようにしたい場合、データベースの該当のテーブルを変更する必要があるということだ。
しかし第1正規形になっていれば、テーブルの変更なしで3つ以上のクラブに加入させることができる。「生徒ID、生徒名、所属クラブ、クラブ指導者」の新しい組み合わせの行を追加したら良いだけだからだ。
2 ステップ2:主キーの一部への依存関係をなくす(第2正規形)
正規化の2つ目のステップでは、主キーの一部への依存関係をなくす。これを専門用語では部分関数従属性をなくすと言う。
修正前 | 修正後 |
---|---|
2−1 第2正規形のポイント
複合キーの片方にしか依存しない値を見つけ、それらを別のそれ専用のテーブルに移動する。
第1正規形
下の画像では、大きく分けて赤枠の情報と青枠の情報の塊があるのがわかる。
それぞれの情報の塊は、以下のような依存関係を持っている。
・赤枠:学生IDがキーであり、学生名がその値に従属している(学生IDで学生名が特定できる)
・青枠:コースIDがキーであり、コース名と教授名がそれに従属している(コースIDでコース名と教授名が特定できる)
第2正規形
第1正規形を第2正規形にすると以下のようになる。まず、赤枠の情報をまとめたテーブルと、青枠の情報をまとめたテーブルに分解する。次に、元々の赤枠と青枠が混在していたテーブルは、それぞれのIDを残してカラムを削除する。
2−2 第2正規形じゃないと困ること
第2正規形じゃないと、以下2つの困ったことが発生する
・関連するデータの片方だけを登録したくてもできない
・データの不整合が簡単に発生する
関連するデータの片方だけを登録したくてもできない
1つ目の困りごとについて説明する。以下の画像のような生徒・コーステーブルの場合を考えよう。
以下のように国語のコースを登録しようとしても、生徒が決まっていないと登録できない。
しかし、第2正規形であればこれが可能だ。コーステーブルにだけデータを追加すれば良い。生徒・コーステーブルには何もデータを追加する必要がないため、生徒がいないコースも登録できる。
データの不整合が簡単に発生する
2つ目の困りごとについて説明する。先ほどと同じ第一正規形のテーブルを例に説明する。念のため、以下に再掲しておく。
以下のように田中さんに歴史を受講してもらうとしよう。このとき、先生の名前を間違って入力してしまう可能性がある。もしくは、先生が変わってデータを修正するときに、変更漏れが発生してしまう可能性もある。
しかし、第2正規形であればこの心配は不要だ。生徒・コーステーブルにだけデータを追加すれば良く、それも「〜〜ID」と言う識別子を使えるので具体的な名前などの入力ミスはあり得ない上、存在しないIDは指定できないから無効なデータが作られることもなくなる。
3 ステップ3:主キーではない項目への依存関係をなくす(第3正規形)
正規化の3つ目のステップでは、主キーではない項目への依存関係をなくす。これを専門用語では推移関数従属性をなくすと言う。
修正前 | 修正後 |
---|---|
3−1 第3正規形のポイント
主キーではない値に従属している値を見つけ、それらの値を専用のテーブルに移す。
第2正規形
下の画像では、主キーではないコース名に教授名が従属している。
第3正規形
第2正規形を第3正規形にするには以下の手順を踏む。
①コースに従属している教授名を別のテーブルに移す。(教授テーブル
②それぞれのレコードにIDを付与する。
③教授テーブルにコースIDを追加する。(もしくは反対に、コーステーブルに教授IDを追加する)
3−2 第3正規形じゃないと困ること
第3正規形になっていない場合、第2正規系になっていない場合と同じ課題が発生する。つまり、以下2つの困ったことが起きる。
・関連するデータの片方だけを登録したくてもできない
・データの不整合が簡単に発生する
関連するデータの片方だけを登録したくてもできない
繰り返しになるため詳細は省略するが、先生だけが増えたことを表したり、コースだけが増えたことを表すことができない。
データの不整合が簡単に発生する
こちらも繰り返しになるため詳細は省略するが、存在しない教授名を適当に入力できてしまうと言う課題がある。
4 あえて正規化をしない事例2選
ここではあえて正規化しない例を紹介する。あえて正規化をしないパターンを認識することで、データに対する認識が豊かになり、実践でも正規化ありきではない柔軟な視点から設計できるようになり、スキルの幅がより一層広がると思う。
基本的に正規化をしない時というのは、以下のようなメリットを求めている時だ。
・データの取得処理が大幅に簡単になる
・DBでの計算処理が減るためパフォーマンス的にプラスに働く
では、具体的にどのようなパターンがあるのかをみていこう。
4−1 集計対象のデータ量が非常に多く、リアルタイム性が求められないデータ
集計対象のデータ量が非常に多い、かつそのデータ自体にリアルタイム性が求められないものも正規化をしない場合がある。そうすることで、以下の2つの利点があるからだ。
例えば、ECサイトでの月次売上の数字をレポートする機能などが挙げられる。例えば以下のような商品テーブルと売上テーブルを持つECサイトを考えてみよう。
商品テーブル | 売上テーブル(一部抜粋) |
---|---|
上記のテーブルから過去5年の月次売上をカテゴリ別に取得したいとすると、かなりの計算量が必要になることが想像できる。
この数字を取得するためにはある程度複雑なSQLやプログラムを書く必要がある。例えばこんなSQLになるだろう。
このような場合に集計用のテーブルを作ってそこにデータを貯めることであえて非正規化することがある。例えば以下のようにだ。
これによって、データ取得のSQLも大幅に簡単になる。
4−2 書き込みがあまりなく、読み込み速度をとにかく重視したい場合
DB自体への書き込みがあまりなく、読み込み速度を重視する場合にあえて正規化しない場合がある。
例としてログデータが挙げられる。ログデータは普通更新されない。また、作成(DBへの書き込み)タイミングも「1日1回」や「数時間おき」などの特定の間隔だけだ。
さらに、ログの解析には読み込み速度が重視される。例えば筆者がサイトユーザの利用ログからサイトの改善案を企画していた時がある。その際はさまざまなログデータをもとに、複数の切り口で分析していた。そのため、ログの検索クエリ(SQL発行)結果が早くわかればわかるほど、一定の時間内で検討できる切り口の数が増えるため、読み込み速度はかなり重視していた。
まとめ
正規化の手順とあえて非正規形を使う場面について説明してきた。
この記事が読者の方のデータベース設計についての知識を豊かにして、より効率的にエンジニアスキルを向上させることに寄与できたらとても嬉しい。