【データベース】RDBの正規化とパフォーマンスの関係を理解する

## はじめに
今回は正規化の欠点や、パフォーマンスとの関係について学んだことをまとめていきたいと思います！

前回の記事はこちら

https://qiita.com/rgj0830/items/4c6db29f15b90c0186a0

### 正規化の欠点
正規化はテーブル設計の基礎ですが、いくつかの欠点もあります。その中でも大きな欠点の一つが、**SQLのパフォーマンスが低下する**ことです。

#### なぜパフォーマンスが低下するのか？
正規化によりテーブルが分割されると、関連するデータを取得する際に複数のテーブルを結合（JOIN）する必要が生じます。JOIN句は計算量が多く、特にレコード数や結合するテーブルが多い場合にはパフォーマンスが低下する原因となります。

- **正規化されたテーブルの特徴**
  - 情報が複数のテーブルに分散しているため、単独のテーブルではユーザーが求める情報を直接取得できないことがある。
  - 無損失分解（データの一貫性を保ちながら情報を分割する操作）によって、情報は失われないが、その結合操作は重い。

#### 解決策
1. **非正規化**
   - テーブルを結合する回数を減らすことで、パフォーマンスの向上を図る。(基本的に非推奨らしい)
2. **SQLのパフォーマンスチューニング**
   - インデックスの最適化やクエリの改善を通じて、パフォーマンスの向上を目指す。

### 正規化と検索・更新処理の関係

- **検索処理**:
  - 非正規化された状態の方が、結合を行わずにデータを取得できるため、検索パフォーマンスが高くなる。

- **更新処理**:
  - 正規化された状態では、冗長性が排除されているため、更新対象のレコードが少なくなり、更新処理の効率が良い（パフォーマンスが優れる）。

**例: 小学校の統合に伴うデータの更新**

- **非正規化の例**: 生徒テーブルに学校名の列を持たせている場合

  | **生徒ID** | **生徒名** | **学校名**   |
  |------------|------------|--------------|
  | 1          | 山田太郎    | 小学校A       |
  | 2          | 鈴木花子    | 小学校A       |
  | 3          | 佐藤次郎    | 小学校A       |
  | 4          | 田中一郎    | 小学校B       |
  | 5          | 中村美咲    | 小学校B       |

  この場合、例えば「小学校A」が「新しい小学校」に統合された場合、全校生徒のレコード（約1000名分）を一括で更新する必要があります。

- **正規化の例** 学校の情報を別のテーブルに分割している場合

  **生徒テーブル**

  | **生徒ID** | **生徒名** | **学校ID** |
  |------------|------------|------------|
  | 1          | 山田太郎    | 1          |
  | 2          | 鈴木花子    | 1          |
  | 3          | 佐藤次郎    | 1          |
  | 4          | 田中一郎    | 2          |
  | 5          | 中村美咲    | 2          |

  **学校テーブル**

  | **学校ID** | **学校名**   |
  |------------|--------------|
  | 1          | 小学校A       |
  | 2          | 小学校B       |

  この場合、学校の統合に伴う変更は「学校テーブル」の「学校ID=1」のレコードのみを更新するだけで済みます。

### どのようにするべきか？
- 基本的には**非正規化を避ける**。
- 非正規化は最終手段とし、他の手段でパフォーマンスの改善ができないかを検討する。
- 整合性や冗長性を犠牲にしてでもパフォーマンスを優先すべき場合に限り、非正規化を行う。

### 正規化のリスク

1. **サマリデータの冗長性排除によるパフォーマンス低下**（サマリデータとは集計したデータのこと）
   - 例えば、ECサイトのデータベースにおいて、「注文」テーブルと「注文明細」テーブルがあるとします。注文日が「注文」テーブルだけに保存されており（冗長性を排除するため）、「注文明細」テーブルには注文に紐づく1つ以上の商品の情報が含まれています。
   - この状況で、特定の注文日の商品数を集計したデータ（サマリデータ）を取得しようとすると、テーブルを結合し、さらにグルーピング処理が必要になります。この処理は計算量が多く、パフォーマンスが低下する原因となります。
   - 一方で、こうした集計データ（サマリデータ）を保持しないことで、更新時の整合性は維持されますが、検索パフォーマンスは低下します。

2. **選択条件の冗長性排除によるパフォーマンス低下**:
   - 「注文」テーブルに加えて「注文明細」テーブルにも注文日を持たせる非正規化の例を考えてみましょう。非正規化することで、特定の日付に基づいた検索やフィルタリングは高速化されます。しかし、正規化された状態では、「注文」テーブルと「注文明細」テーブルの間で結合が必要になり、この結合処理が重い処理となり、パフォーマンスが低下します。

### 非正規化のリスク

1. **更新時のパフォーマンス低下**
   - 非正規化により、例えばサマリデータを追加すると、関連するレコードが追加、削除、または変更されるたびに集計データを更新し直す必要があります。これにより、システムの負荷が大幅に増加します。

2. **データの鮮度の低下**
   - 非正規化したデータを更新するタイミングが遅れると、データの鮮度が低下します。リアルタイムでの更新を行うとシステムに負荷がかかりすぎるため、定期的に更新する形を取ることが多いです。しかし、定期更新の場合、更新が行われる前のデータは最新の情報を反映していないことになります。

### まとめ
今回は、正規化によるリスクやデメリットについて学びました。確かに、正規化によってパフォーマンスが低下することもありますが、それでも非正規化によるリスクやデメリットの方が大きいように感じました。

システムの規模やテーブルの使用頻度、関連するテーブルのレコード数などの前提条件によって、適切なアプローチが変わるため、一概には言えないのが難しいなと感じました。たとえば、大規模なシステムでは非正規化によるパフォーマンス向上が必要になる場合もあるのだろうか？でもデータの整合性が取れなかったり、扱いが複雑になったりするリスクも大きいだろうし、そういったケースが実際にあるのかどうかは気になりました。

なので基本的には非正規化を避け、正規化した状態でSQLの最適化やインデックスの見直しなど、他の方法でパフォーマンス改善を試みる方が良いのだろうなと。ただ、その辺は、まだまだ知識が乏しいので引き続き学んでいきたいと思います！

### 参考書籍

https://www.shoeisha.co.jp/book/detail/9784798186627