0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

データベースにおける三つの正規形と、それを無視する理由

Posted at

表紙

データベースの三大正規形は、データベース設計における最も基本的な 3 つの規範です。それでは、三大正規形とは何でしょうか?実際の開発において、必ず三大正規形を厳密に守る必要があるのでしょうか?本記事では、これらについて一緒に考えていきましょう。

三大正規形

1. 第一正規形(1NF:各列の原子性を保証)

第一正規形では、データベース内のすべてのテーブルの各フィールド(列)が原子的であること、つまりその値がこれ以上分割できないことを要求します。言い換えると、各フィールドには単一の値しか格納できず、集合や配列、重複したグループを含めることはできません。

例として、次のような学生テーブル Student を考えてみましょう:

学生 ID 氏名 電話番号
1 Alice 123456789, 987654321
2 Bob 555555555

このテーブルでは、電話番号のフィールドに複数の番号が含まれており、1NF の原子性要件に違反しています。1NF を満たすには、電話番号を個別のレコードに分けるか、新たなテーブルを作成する必要があります。

1NF を満たす設計:

学生テーブル Student

学生 ID 氏名
1 Alice
2 Bob

電話番号テーブル Phone

電話 ID 学生 ID 電話番号
1 1 123456789
2 1 987654321
3 2 555555555

2. 第二正規形(2NF:すべての列が主キーに完全に従属)

第二正規形では、第一正規形を満たしたうえで、テーブル内の部分従属を排除することが求められます。つまり、非主キー列が主キー全体に完全に依存している必要があり、主キーの一部にのみ依存している場合は不適切です。これは主に複合主キーを持つ場合に適用されます。

例として、次のような注文明細テーブル OrderDetail を考えます:

注文 ID 商品 ID 商品名 数量 単価
1001 A01 りんご 10 2.5
1001 A02 オレンジ 5 3.0
1002 A01 りんご 7 2.5

このテーブルでは、主キーは複合主キー(注文 ID, 商品 ID)です。商品名と単価は商品 ID のみに依存しており、複合主キー全体には依存していないため、部分従属が存在し、2NF に違反しています。

2NF を満たす設計:

注文明細テーブル OrderDetail

注文 ID 商品 ID 数量
1001 A01 10
1001 A02 5
1002 A01 7

商品テーブル Product

商品 ID 商品名 単価
A01 りんご 2.5
A02 オレンジ 3.0

3. 第三正規形(3NF:すべての列が主キーに直接従属)

第三正規形では、第二正規形を満たすことに加え、テーブル内の推移的従属を排除することが求められます。つまり、非主キー列が他の非主キー列に依存してはいけません。すべての非主キー列は、主キーに直接依存していなければなりません。

次のような従業員テーブル Employee を例に考えてみます:

従業員 ID 氏名 部門 ID 部門名
E01 Alice D01 営業部
E02 Bob D02 技術部
E03 Charlie D01 営業部

このテーブルでは、部門名は部門 ID に依存しており、部門 ID は主キーである従業員 ID に依存しています。これは推移的従属に該当し、3NF に違反しています。

3NF を満たす設計:

従業員テーブル Employee

従業員 ID 氏名 部門 ID
E01 Alice D01
E02 Bob D02
E03 Charlie D01

部門テーブル Department

部門 ID 部門名
D01 営業部
D02 技術部

部門情報を別テーブルに分離することで、推移的従属を排除し、第三正規形に適合したデータベース構造になります。

最後に、データベース設計における三大正規形をまとめます:

  • 第一正規形(1NF):各フィールドの値が原子的であり、分割できないことを保証します。
  • 第二正規形(2NF):1NF を満たした上で、部分従属を排除し、すべての非主キー列が主キー全体に完全に依存していることを保証します。
  • 第三正規形(3NF):2NF を満たした上で、推移的従属を排除し、すべての非主キー列が主キーに直接依存していることを保証します。

正規形の破壊

実際の業務においては、第一正規形(1NF)、第二正規形(2NF)、第三正規形(3NF)を遵守することで、データの一貫性が向上し、冗長性が減少します。しかしながら、パフォーマンスの要求、設計の簡略化、特定のビジネス要件を満たすために、これらの正規形に違反する必要がある場合もあります。

以下は、正規形を意図的に破壊する一般的な理由と、それに伴う例です。

パフォーマンス最適化

高トラフィックで大量データを扱うアプリケーションにおいて、厳密な正規化は頻繁なテーブル結合を引き起こし、クエリの時間やシステム負荷を増加させる可能性があります。クエリパフォーマンスを向上させるために、設計者はデータの冗長化によって結合操作を減らすことがあります。

たとえば、EC システムにおいては、注文テーブル(Orders)とユーザーテーブル(Users)が存在します。厳密に 3NF に基づく設計では、注文テーブルはユーザー ID のみを保持し、詳細情報は結合によって取得する必要があります。

しかし、クエリ性能を重視する場合、注文テーブルにユーザーの氏名や住所などを冗長に保存することがあります。これにより、Users テーブルとの結合を回避し、注文情報の取得が高速化されます。

3NF を破壊した設計例:

注文 ID ユーザー ID 氏名 住所 注文日 合計金額
1001 U01 Alice New York 2025-01-01 500
1002 U02 Bob Los Angeles 2025-01-02 300

クエリと開発の簡略化

厳格な正規化は、テーブル構造を複雑にし、開発や保守の負担を増加させることがあります。クエリロジックを簡素化し、開発の複雑さを減らすために、適度な冗長性を許容するケースもあります。

例えば、CMS(コンテンツ管理システム)において、記事テーブル(Articles)とカテゴリテーブル(Categories)は通常分離されますが、頻繁にカテゴリ名を表示する必要がある場合、結合クエリが煩雑になる可能性があります。そのため、Articles テーブルにカテゴリ名を直接保存することで、フロントエンドでの表示を簡略化し、開発工数を削減できます。

3NF を破壊した設計例:

記事 ID タイトル 内容 カテゴリ ID カテゴリ名
A01 記事一 ... C01 技術
A02 記事二 ... C02 生活

レポートとデータウェアハウス

データウェアハウスやレポートシステムでは、大量のデータを迅速に読み取り集計する必要があります。クエリ性能とデータ分析を最適化するために、冗長なデータ構造を採用することがあり、スタースキーマやスノーフレークスキーマといった正規形に必ずしも従わない設計が用いられます。

販売データウェアハウスにおいて、販売レポートを迅速に生成するため、ディメンション情報を含むファクトテーブルを作成することがあります。

3NF を破壊した設計例:

販売 ID 製品 ID 製品名 カテゴリ 販売数量 販売金額 販売日
S01 P01 携帯電話 電子 100 50000 2025/1/1
S02 P02 書籍 教育 200 20000 2025/1/2

このファクトテーブルでは、製品名やカテゴリを直接保存することで、ディメンションテーブルとの結合を省略し、レポート作成の効率を高めています。

特殊なビジネスニーズ

ある特定のビジネスシーンでは、特定のクエリや処理に迅速に対応する必要があります。このような場合、適度な冗長化によって要件を満たすことができます。

たとえば、リアルタイム取引システムでは、ユーザーの口座残高をすぐに算出する必要があるため、各トランザクションから都度計算するのではなく、ユーザーテーブルに現在の残高を直接保持する場合があります。

3NF を破壊した設計例:

ユーザー ID ユーザー名 現在残高
U01 Alice 10000
U02 Bob 5000

トランザクション履歴テーブルには各取引の増減が記録されますが、ユーザーテーブルに現在残高を持たせることで、クエリ時の複雑な計算を避けることができます。

読み取りと書き込みのバランス

あるアプリケーションでは、読み取り操作が書き込み操作より圧倒的に多い場合があります。読み取り性能を重視するため、データの冗長性を許容し、クエリ速度を向上させることが選ばれることもあります。

たとえば、SNS プラットフォームでは、ユーザーの友達数がプロフィールに頻繁に表示されます。毎回リアルタイムで友達数を計算するのは効率的ではありません。そのため、ユーザーテーブルに「友達数」フィールドを冗長に持たせる設計が取られることがあります。

3NF を破壊した設計例:

ユーザー ID ユーザー名 友達数
U01 Alice 150
U02 Bob 200

Users テーブルに友達数を冗長に保存することで、リアルタイム計算を不要にし、表示を高速化できます。

素早いイテレーションと柔軟性

急速に成長するプロダクトやスタートアップ企業では、データベース設計の頻繁な変更が求められます。過度な正規化は柔軟性に欠け、開発のスピードを妨げることがあります。適切な冗長設計は、開発の柔軟性と速度を向上させることができます。

たとえば、スタートアップの EC プラットフォームが短期間でサービスをローンチする場合、ユーザーの配送先住所を注文テーブルに直接保存し、別途住所テーブルを作成しない選択をすることがあります。

3NF を破壊した設計例:

注文 ID ユーザー ID ユーザー名 配送先住所 注文日 合計金額
O1001 U01 Alice New York 2025/1/1 800
O1002 U02 Bob Los Angeles 2025/1/2 1200

このような設計により、迅速なリリースが可能となり、将来的な要件に応じて正規化と最適化を行うことができます。

設計の複雑さを抑え、理解しやすくする

時には、過度な正規化がデータベース構造を複雑にし、理解や保守が困難になることがあります。適度な冗長化は、設計の複雑さを抑え、チームによる構造の理解やコミュニケーションを容易にします。

たとえば、学校管理システムにおいて、学生のクラス情報を複数のテーブルに分けると、構造の理解が難しくなる可能性があります。そのため、設計を簡素化するために、学生テーブルに直接クラス名を保存する場合があります。

3NF を破壊した設計例:

学生 ID 氏名 クラス ID クラス名 担任
S01 Alice C01 Class A Charlie
S02 Bob C02 Class B David

学生テーブルにクラス名や担任教師を直接保存することで、テーブル数が減り、設計が簡素になります。

まとめ

本記事では、データベースにおける三つの正規形とその具体例について解説しました。これらはデータベース設計の基本的なルールですが、実務においてはパフォーマンスの向上、設計の簡略化、素早い開発、あるいは特定のビジネス要件を満たすために、必ずしも三正規形を厳密に守るとは限りません。

つまり、システムアーキテクチャの設計は、ビジネス要件、データ整合性、パフォーマンス、開発効率など、さまざまな要素のバランスによって決まるものです。私たちは具体的なアプリケーションのシナリオに応じて、合理的な設計判断を下す必要があります。


私たちはLeapcell、バックエンド・プロジェクトのホスティングの最適解です。

Leapcell

Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです:

複数言語サポート

  • Node.js、Python、Go、Rustで開発できます。

無制限のプロジェクトデプロイ

  • 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

比類のないコスト効率

  • 使用量に応じた支払い、アイドル時間は課金されません。
  • 例: $25で6.94Mリクエスト、平均応答時間60ms。

洗練された開発者体験

  • 直感的なUIで簡単に設定できます。
  • 完全自動化されたCI/CDパイプラインとGitOps統合。
  • 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

簡単なスケーラビリティと高パフォーマンス

  • 高い同時実行性を容易に処理するためのオートスケーリング。
  • ゼロ運用オーバーヘッド — 構築に集中できます。

ドキュメントで詳細を確認!

Try Leapcell

Xでフォローする:@LeapcellHQ


ブログでこの記事を読む

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?