テーブルの正規化(Normalization)は、データベースデザインのプロセスで、データの冗長性を減少させ、データの整合性を保つための重要なステップです。正規化は通常、第1正規化(1NF)、第2正規化(2NF)、第3正規化(3NF)の3つの主要な段階で実行されます。以下でそれぞれの正規化について説明します。
第1正規化(1NF)
第一正規化は、テーブル内の各列がアトミック(分割不可)な値を持つことを確保するプロセスです。つまり、テーブル内の各セルには、重複するデータがないようにすることが目的です。第一正規化の主なルールは次のとおりです:
各テーブル内の各列は、単一の値か、その値のセット(リスト、配列など)を持つべきです。各行(レコード)は一意の識別子(主キー)を持つべきです。
例えば、以下の非正規化のテーブルを考えてみましょう:
非正規化テーブル (Unnormalized Table):
studentID | studentName | subject |
1 | Alice | 数学, 物理 |
2 | Bob | 化学 |
このテーブルは、科目(subject)の列に複数の値を持っており、非正規化です。第一正規化を適用すると、別々のテーブルにデータを分割し、各列がアトミックな値を持つようにします。
第一正規化後のテーブル (1NF Table):
学生テーブル (Students):
studentID | studentName |
1 | Alice |
2 | Bob |
科目テーブル (student_subjects):
studentID | subjectID | subject |
1 | 1 | 数学 |
1 | 2 | 物理 |
2 | 3 | 化学 |
studentIDとsubjectIDの組み合わせで、ユニークなキーとなります。
これにより、各セルはアトミックな値を持ち、データの整合性が向上します。
ただsubjectに着目すると、subject(非キー)は、subjectIDには依存しますが、studentIDとsubjectID全体(主キー)には依存していません。
第2正規化(2NF)
第二正規化は、テーブルが第一正規化であることを前提として、テーブル内の部分関数従属(Partial Functional Dependency)を解消するプロセスです。部分関数従属とは、主キーの一部の列が他の列に依存している場合のことを指します。第二正規化の主要なルールは次のとおりです:
テーブル内の各非キー属性(主キーでない属性)は、主キー全体に依存すべきです。
第一正規化した科目テーブル(student_subjects)テーブルには、subjectがあり、こちらはsubjectIDには依存しますが、studentIDには依存していません。
非正規化テーブル (Unnormalized Table):
studentID | subjectID | subject |
1 | 1 | 数学 |
1 | 2 | 物理 |
2 | 3 | 化学 |
2 | 1 | 数学 |
そのため、以下のように第二正規化することができます。
第二正規化後のテーブル (2NF Table):
中間(pivot)テーブル(student_subjects):
studentID | subjectID |
1 | 1 |
1 | 2 |
2 | 3 |
2 | 1 |
科目テーブル(subjects):
subjectID | subject |
1 | 数学 |
2 | 物理 |
3 | 化学 |
studentsとsubjectsの関係は、多対多(ManyToMany)となるため、通常中間テーブル(pivot)を設けることで多対多の関係を実現します。
データモデルについては、以下の記事で解説しています。
また別の例として、以下のテーブルを考えてみましょう:
非正規化テーブル (Unnormalized Table):
orderID | orderDate | customerID | name | productID | productName | quantity |
101 | 2023/01/11 | 1 | Alice | 1 | Macbook Pro | 1 |
101 | 2023/01/11 | 1 | Alice | 2 | iphone13 | 2 |
102 | 2023/02/12 | 2 | Bob | 2 | iphone13 | 1 |
103 | 2023/03/13 | 1 | Alice | 3 | AirPod buds | 2 |
orderIDとproductIDの組み合わせで一意に特定できるテーブルとなっていて、productNameはproductIDに依存して、quantityは、orderIDとproductIDに依存します。
第二正規化後のテーブル (2NF Table):
注文テーブル (orders):
orderID | orderDate | customerID | name |
101 | 2023/01/11 | 1 | Alice |
102 | 2023/02/12 | 2 | Bob |
103 | 2023/03/13 | 1 | Alice |
商品テーブル (products):
productID | productName |
1 | Macbook Pro |
2 | iphone13 |
3 | AirPod buds |
中間テーブル (order_products):
orderID | productID | quantity |
101 | 1 | 1 |
101 | 2 | 2 |
102 | 2 | 1 |
103 | 3 | 2 |
このように3つのテーブルにわけることで、productName(非キー)は、productID(キー)にのみ依存する関係になりました。
ただ、注文テーブル (orders)に着目すると、customerIDとnameが含まれていて、これらは非キーとなっています。またname(非キー)がcutomerID(非キー)に依存する関係となっています。
第3正規化(3NF)
第三正規化は、テーブルが第二正規化であることを前提として、推移的関数従属(Transitive Dependency)を解消するプロセスです。推移的関数従属とは、非キー属性が他の非キー属性に依存している場合を指します。第三正規化の主要なルールは次のとおりです:
テーブル内の各非キー属性は、主キーまたは他の非キー属性にのみ依存すべきです。
第二正規化した注文テーブル (orders)を第三正規化していきます。
非正規化テーブル (Unnormalized Table):
orderID | orderDate | customerID | name |
101 | 2023/01/11 | 1 | Alice |
102 | 2023/02/12 | 2 | Bob |
103 | 2023/03/13 | 1 | Alice |
第三正規化後のテーブル (3NF Table):
注文テーブル (orders):
orderID | orderDate | customerID |
101 | 2023/01/11 | 1 |
102 | 2023/02/12 | 2 |
103 | 2023/03/13 | 1 |
顧客テーブル(customers):
customerID | name |
1 | Alice |
2 | Bob |
このように、ordersテーブルにてnameが、cutomerIDに依存していた関係を、customerテーブルに分けたことによって、データの整合性が向上します。第三正規化は、データの冗長性を排除し、データベースのメンテナンスとクエリの効率性を向上させるのに役立ちます。
まとめ
上記の例は、簡単な例になるので、場合によっては1つのテーブルで管理してしまっても問題ないかもしれません。
ただ例えば、1テーブルに100カラムあるのに、実際にある機能で使用するカラムはその内の10カラムであれば、残りの90カラムは無駄になります。
そのため、不要なカラムは別のテーブルに持たせて、必要に応じて、テーブル結合した方が効率的であり、データをメンテナンスしやすくなります。
SQLのテーブル設計を考える上でデータモデルや正規化という考えは重要になります。
以下の記事ではSQLを体系的に学習できるオンラインコースの概要を紹介しています。