データベースの設計を改善!3NFとBCNFの実践的な使い分け
3NFとBCNFの違い:分かりやすく解説
リレーショナルデータベースは、情報を表形式で保存するデータベースです。エクセルのようなイメージです。
データベース正規化は、データベースを効率的に管理するために、データを整理する規則です。本棚の本を整理整頓して、必要な情報を見つけやすくするようなイメージです。
3NFとBCNFは、データベース正規化の規則のうち、よく使われる2つのレベルです。
3NFは、以下の3つの条件を満たす必要があります。
- すべての属性が主キーに部分的に依存している
- 主キー以外の属性は、遷移的依存をしていない
決定とは、ある属性の値が決まれば、他の属性の値も決まってしまう関係のことです。
推移的依存とは、ある属性が別の属性に依存し、その別の属性がさらに別の属性に依存している関係のことです。
推移的閉包とは、ある属性が直接的または間接的に依存しているすべての属性の集合のことです。
3NFとBCNFの違い
3NFは、主キー以外の属性が、主キーに直接的に依存しているかどうかで判断します。
BCNFは、3NFよりも厳格な規則です。
例
以下の表は、社員情報データベースです。
社員番号 | 名前 | 部署 | 役職 | 給与 |
---|---|---|---|---|
1 | 山田太郎 | 営業部 | 営業 | 30万円 |
2 | 佐藤花子 | 経理部 | 経理 | 25万円 |
3 | 田中一郎 | 開発部 | 開発 | 40万円 |
この表は、3NFですが、BCNFではありません。
理由
- 属性役職は、属性部署に決定されています。
つまり、属性給与は、属性部署に推移的に依存しています。
BCNFにするために
この表をBCNFにするためには、以下の2つの方法があります。
- 表を分割する
- 属性給与を部分キーにする
表を分割すると、以下の2つの表になります。
社員情報表
社員番号 | 名前 | 部署 |
---|---|---|
1 | 山田太郎 | 営業部 |
2 | 佐藤花子 | 経理部 |
3 | 田中一郎 | 開発部 |
役職情報表
部署 | 役職 | 給与 |
---|---|---|
営業部 | 営業 | 30万円 |
経理部 | 経理 | 25万円 |
開発部 | 開発 | 40万円 |
属性給与を部分キーにすると、以下のようになります。
社員番号 | 名前 | 部署 | 役職 | 給与 |
---|---|---|---|---|
1 | 山田太郎 | 営業部 | 営業 | 30万円 |
2 | 佐藤花子 | 経理部 | 経理 | 25万円 |
3 | 田中一郎 | 開発部 | 開発 | 40万円 |
補足
- データベース正規化には、3NFとBCNF以外にも、さまざまなレベルがあります。
- データベース正規化は、データ
# 社員情報データベース
class Employee:
def __init__(self, employee_id, name, department, position, salary):
self.employee_id = employee_id
self.name = name
self.department = department
self.position = position
self.salary = salary
# 部署情報データベース
class Department:
def __init__(self, department_id, name):
self.department_id = department_id
self.name = name
# 役職情報データベース
class Position:
def __init__(self, position_id, name, salary):
self.position_id = position_id
self.name = name
self.salary = salary
# サンプルデータ
employees = [
Employee(1, "山田太郎", "営業部", "営業", 300000),
Employee(2, "佐藤花子", "経理部", "経理", 250000),
Employee(3, "田中一郎", "開発部", "開発", 400000),
]
departments = [
Department(1, "営業部"),
Department(2, "経理部"),
Department(3, "開発部"),
]
positions = [
Position(1, "営業", 300000),
Position(2, "経理", 250000),
Position(3, "開発", 400000),
]
BCNFのサンプルコード
# 社員情報データベース
class Employee:
def __init__(self, employee_id, name, department_id, position_id):
self.employee_id = employee_id
self.name = name
self.department_id = department_id
self.position_id = position_id
# 部署情報データベース
class Department:
def __init__(self, department_id, name):
self.department_id = department_id
self.name = name
# 役職情報データベース
class Position:
def __init__(self, position_id, name, salary):
self.position_id = position_id
self.name = name
self.salary = salary
# サンプルデータ
employees = [
Employee(1, "山田太郎", 1, 1),
Employee(2, "佐藤花子", 2, 2),
Employee(3, "田中一郎", 3, 3),
]
departments = [
Department(1, "営業部"),
Department(2, "経理部"),
Department(3, "開発部"),
]
positions = [
Position(1, "営業", 300000),
Position(2, "経理", 250000),
Position(3, "開発", 400000),
]
解説
3NFのサンプルコードでは、Employee
クラスには、department
属性とposition
属性があります。
これらの属性は、employee_id
属性に直接的に依存しています。
これらの属性は、employee_id
属性ではなく、department
属性とposition
属性に依存しています。
3NFとBCNFを満たすその他の方法
属性の追加
BCNFを満たすために、属性給与を部分キーにする方法以外にも、属性部署と役職の組み合わせを主キーにする方法があります。
この方法では、以下のようになります。
社員番号 | 名前 | 部署 | 役職 | 給与 |
---|---|---|---|---|
1 | 山田太郎 | 営業部 | 営業 | 30万円 |
2 | 佐藤花子 | 経理部 | 経理 | 25万円 |
3 | 田中一郎 | 開発部 | 開発 | 40万円 |
属性の削除
3NFを満たすために、属性役職を削除する方法があります。
社員番号 | 名前 | 部署 | 給与 |
---|---|---|---|
1 | 山田太郎 | 営業部 | 30万円 |
2 | 佐藤花子 | 経理部 | 25万円 |
3 | 田中一郎 | 開発部 | 40万円 |
冗長性の許容
例えば、以下の表は、3NFを満たしていませんが、冗長性を許容することで、BCNFを満たすことができます。
社員番号 | 名前 | 部署 | 役職 | 給与 |
---|---|---|---|---|
1 | 山田太郎 | 営業部 | 営業 | 30万円 |
2 | 佐藤花子 | 経理部 | 経理 | 25万円 |
3 | 田中一郎 | 開発部 | 開発 | 40万円 |
4 | 高橋二郎 | 営業部 | 営業 | 30万円 |
database relational-database database-normalization