なんとなくやっていた正規化を型化していく

## はじめに
皆さん、こんにちは。

私は社内エンジニアとして、コードを書くようになってから、約7年ぐらい経ちますが、あまり体系的な技術手段を学んできていません。
正規化もその１つ。

初めてDB設計した時は、当時外注していた大手ベンダーさんや先輩社員が作成したDB設計を見様見真似でやってました。そんな複雑なものでもなかったですし、拙いDB設計でも問題にはなりませんでした。

しかし、最近は複雑なドメインのシステムを扱う機会が多く、付け焼き刃な知識では歯が立たなくなってきました。
というか、今まさに歯が立たない状態です。（笑）

なので、ここで正規化をちゃんと自分の中で整理し、習得しようという意気込みです。

認識に誤りがあれば、是非ご指摘お願いします:bow_tone1:

## 正規化とは？
データの冗長性を除いていき、無駄をなくしたデータに成形していくことです。

正規化の目的は大きく２点あります。

* データを現実世界のオブジェクトとして喩え、人間が理解しやすい形で管理できる（=保守性や拡張性の向上）
* データの異常、不整合を防ぐ

正規化されたデータを「正規形」、されていないデータを「非正規形」と言います。

以下が非正規形のデータの例です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446358/10b727d7-f646-99c7-5597-527983168340.png)

このようなテーブルをリレーショナルデータベースで見かけた場合、真っ先に正規化の対象になると思いますが、あえて正規化しないケースもあるようです。

* パフォーマンスを最も優先しなければいけない場面
* ログ等の集積しておいて後で分析するためのデータを残す場面

正規形のデータに比べて非正規形はパフォーマンスが高いと言われます。

例えば、ある事象に対するデータを取得するのに1テーブルのデータを出力するだけの方が、複数テーブルを結合するよりもクエリは単純ですし、実行速度が速いのも明らかだと思います。

また、ログ等のデータ挿入後、更新する必要がないデータについても、データ異常や不整合は起きにくく、正規化の恩恵を受けづらいですし、非正規形のままの方がかえって扱いやすいため、正規化しない選択肢が取られるようです。

以上が非正規形のメリットになるようです。
逆に言うと、以上の場面を除いては基本的に正規化を行う必要があると考えてください。

正規化には５段階のレベルが存在し、正規化の進行度合いを表します。

今回は、第３正規形までの正規化を、先程説明で利用した非正規形なデータに対し適用しながら、正規化の内容を整理していきたいと思います。

## 第１正規形

第１正規化で行うことは、主に横方向の繰り返しの整理です。

SQLアンチパターンの例でいうと
* マルチカラムアトリビュート
* ジェイウォーク
で、1つの属性に複数の値を持たせることが、横方向の繰り返しを生んでしまう要因となります。

先程の非正規形のデータで該当するのは、「装置１」「装置２」の部分になるかと思います。
リレーショナルデータベースは、性質上、動的に横に列を増やしていくのは難しいです。
ですので、横に連続するデータは縦に並ぶように整理してみましょう。

適用すると、以下のような形になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446358/e284ca72-9a77-8be2-3eb9-87fa41aade19.png)

これで横方向の繰り返しの排除ができました。

## 第2正規形

第２正規化で行うことは、部分関数従属の分離です。

部分関数従属とは、例えば「装置型番が決まると装置名称が決まる」といったことを表し、「装置名称は装置型番に関数従属している」と言えます。

では、まず先程の第1正規形のデータで主キーになる列を探しましょう。

* 注文番号
* 取引先コード
* 装置型番

次に、これらに紐づく情報を整理しましょう。

* 注文番号 -> 注文日付、取引先コード、取引先名称、取引先住所
* 取引先コード -> 取引先名称、取引先住所
* 装置型番 -> 装置名称、装置単価
* 注文番号+装置型番 -> 台数、金額

では、上記の関数従属に応じて、テーブルを分けてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446358/f588f8fc-5961-0296-71ee-7c7a2bd8fcd5.png)

取引先については、テーブルを分けていません。
これについては、次の第3正規形でお話します。

## 第3正規形

第３正規化では、さらに推移的関数従属と呼ばれる従属関係の分離を行います。

注文書テーブルを例に推移的関数従属について説明します。

* 注文番号（X）が決まれば取引先（Y）が決まる。（X -> Y）
* 取引先（Y）が決まると取引先名称（Z）が決まる。（Y -> Z）
* つまり、注文番号（X）が決まると取引先名称（Z）が決まる。（X -> Y -> Z = X -> Z）

このような状態を「取引先名称は、注文番号に推移的関数従属がある」と言えます。

では、上記を踏まえ、第2正規形に反映させてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446358/27f680fa-9602-42c1-543c-786ddd41cc3f.png)

完全関数従属なテーブルになりました。

## 最後に

DB設計にあたり、正規化するのは当たり前のものだと思っていましたが、非正規化におけるメリットもあるというのは意外な話でした。
当然使うタイミングを誤れば、大きな負債を生みかねない判断なので、その点は気をつけて活用していきたいです。

また、非正規形のデータを見て思ったのは、未だにこういうデータ見かけるなぁと。
例えば、情シスが関与していない、現場が管理しているExcelとか。
世の中に溢れているサービスは、Webベースが当たり前になりつつありますが、業務システム界隈では未だにExcelベースのシステム、データがたくさんあります。
そういったシステムを刷新する際には、正規化の手順を追っていけば正しくDB設計に落とし込めそうですね。

逆に全く新しいシステムの場合は、最終的に必要なデータを導き出してから、正規化していくという流れになりそうです。

今回は、第３正規形までの説明になりましたが、ボイスコッド、第４、第５についてもそのうち触れていきたいと思います。

## 引用
[SQLアンチパターン](https://www.amazon.co.jp/SQL%E3%82%A2%E3%83%B3%E3%83%81%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-Bill-Karwin/dp/4873115892/ref=asc_df_4873115892/?tag=jpgo-22&linkCode=df0&hvadid=295678107984&hvpos=&hvnetw=g&hvrand=17079481884436280262&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009312&hvtargid=pla-524364716062&psc=1&th=1&psc=1)
[Youtube - 第1正規形](https://youtu.be/c2LdmWAslBw)
[Youtube - 第2正規形](https://youtu.be/1K6EM3woxms)
[Youtube - 第3正規形](https://youtu.be/aYjxNQRQyKs)
[リレーショナルデータベース正規形の種類と正規化手順
](https://proengineer.internous.co.jp/content/columnfeature/6480#section100)