CA Tech Loungeの2期生でバックエンドエンジニアとして活動している@tf63です.本記事ではデータベースのテーブルの正規化について端的に紹介します.
この記事は CA Tech Lounge Advent Calendar 2023 の19日目の記事になります.
正規化
テーブルの正規化とは,テーブルを冗長性が排除された形(正規形)に変換する操作です.正規化が必要とされる理由は,データに冗長性が含まれるとデータ登録時や更新時に不整合が生じることがあるためです.
例として,学部IDと学生IDを主キーに持つテーブルを考えてみましょう.このテーブルには冗長性が含まれます.
サークル名はサークルIDによって一意に決定可能なため,サークル名の列は情報としては不要です.このような冗長性が含まれるとサークル名を更新したい時に不都合が生じます.
例えば「ゆるゆるサッカー部」の運営方針が変わり「ガチガチサッカー部」になったとしましょう.サークル名を更新するには「ゆるゆるサッカー部」に所属する学生を全て探し出す必要がありますが,全ての情報が正しく更新される保証はあるでしょうか?
テーブルを分割し,次のように冗長性を排除すれば不都合なくサークル名を更新できます.
このように,正規化はデータの冗長性を排除し,今後の操作の一貫性と効率性を保証するために行われます.
正規化の欠点
冗長性を排除することには勿論欠点もあります.先の例において,主キーから学生の氏名とサークル名を取得する場合を考えましょう.正規化をした場合にはサークル名を取得するために分割したテーブルを結合する必要があります.テーブルの結合は比較的重い操作であるため,正規化によってパフォーマンスは低下してしまいます.
関数従属性
先の例では,「ある列が別の列によって一意に決定可能であること」に焦点を当てて冗長性を排除しました.この性質は関数従属性と呼ばれます.関数従属性は正規化を理解するために重要な性質であり,以降では関数従属性の考え方に着目して正規形について紹介します.
正規形
正規形にはレベルがあり,第一正規形 ~ 第五正規形がよく知られています.このうち,第一正規形 ~ 第三正規形への正規化は可逆であり情報が失われません(無損失分解).本記事では第一正規形 ~ 第三正規形について紹介します.
第一正規形
第一正規形では全ての列が主キーに関数従属となるようにします.
第一正規形でないテーブルは次のようなテーブルです.このテーブルでは,複数のサークルに所属している学生をサークルIDの列で配列として表現しています.しかし,これでは主キーを決めても学生のサークルIDが一意に定まらず,サークルIDを登録・更新するときに不効率になります.
次のようにテーブルを分割することで第一正規形にできます.
第二正規形
第二正規形では全ての列が主キーを構成する全ての列に関数従属となるようにします.この状態を完全関数従属と呼びます.
第二正規形でないテーブルは次のようなテーブルです.このテーブルは学部IDと学生IDを主キーとしていますが,氏名の列は列{学部ID, 学生ID}にのみ関数従属であるのに対し,学部名の列は列{学部ID}に関数従属となっています.これは部分関数従属と呼ばれます.
部分関数従属であるとデータ登録・更新に不都合があります.例えば,新しく学部IDと学部名を登録したいとしましょう.このとき,学生IDと氏名は不要なはずですが,学生IDは主キーの一部でありnullには出来ません.データ更新も先の例と同様に不都合があります.
次のようにテーブルを分割することで第二正規形にできます.
第三正規形
第三正規形では推移的関数従属を排除します.推移的関数従属とは
主キー → 非主キー → 非主キー
のような関数従属が含まれることです.
第三正規形でないテーブルの例は最初に示したテーブルです.このテーブルは {学部ID, 学生ID} → サークルID → サークル名の列が推移的関数従属です.
推移的関数従属であると先の例と同様にデータ登録・更新に不都合があります.
次のようにテーブルを分割することで第三正規形にできます.
以上です.
参考