君は心理学者なのか?

大学時代に心理学を専攻しなぜかプログラマになった、サイコ(心理学)プログラマかろてんの雑記。

なぜ、データベースを正規化するのか。正規化のメリットについてソースコードレベルで考えてみる(第一正規化)

正規化のメリットについてソースコードレベルで考えてみる

DB設計をする際、あたりまえのように正規化していたのですが、

「なぜ正規化するか」についてはあまり良くわかっていませんでした。

調べると

正規化の理論は、

データの冗長性を排除し、

更新時の整合性を維持しやすくすることを目指しています。

とあるのですが、抽象的でイマイチよくわかりません。

そこで今回は、

正規化した場合としなかった場合を、

仕様変更時にどれだけ大変か

という観点で比較してみました。

実験

RPGの主人公がアイテムを3つだけ持つことができる」

という仕様でDB設計を行います。

また、主人公の情報とアイテムの情報をviewファイルに書き出すことにします。

A. 正規化をしなかった場合

DB(Heroテーブル)

# 物理名 論理名
1 id id
2 名前 name
3 レベル level
4 アイテム1の名前 item1_name
5 アイテム2の名前 item2_name
6 アイテム3の名前 item3_name

コントローラ

Heroを1人取得しておきます。

class HeroController
  def index
    @hero = Hero.find(1)
  end
end

view

<%= @Hero.name %>
<%= @Hero.age %>
<%= @Hero.level %>

<%= @Hero.item1_name %>
<%= @Hero.item2_name %>
<%= @Hero.item3_name %>
仕様変更

このような状態で、

「Heroのアイテムを1つ増やしたい」という仕様変更があると、

* DBのカラムにitem4_nameを増やす

* viewファイルにitem4_nameを出力するように修正

という変更が必要となってきます。

今回の場合、1コントローラ1viewなので修正範囲は狭いですが、

このテーブルが多くのコントローラ・viewで参照されている場合修正箇所はどんどん増えます。

またこの修正は、Heroのアイテムを増やすたびに行われます。

-- なんだかおかしなことになってきましたね。

B. 正規化する場合

そこでDBを第一正規化してみます。

第1正規化では、同一の情報のグループが繰り返し出現している部分を分離します。

www.atmarkit.co.jp

DB(Hero, Itemテーブル)

Heroテーブル
# 物理名 論理名
1 id id
2 名前 name
3 レベル level
Itemテーブル
# 物理名 論理名
1 id id
2 HeroId hero_id
3 アイテム名 name

controller

class HeroController
  def index
    @hero = Hero.find(1)
    @items = Item.where(hero_id: 1)
  end
end

view

<%= @Hero.name %>
<%= @Hero.age %>
<%= @Hero.level %>

<%= @items.each do |item| %>
  <%= item.name %>
<% end %>
特徴

正規化されていないテーブルから、

「繰り返しが発生しているデータ(Item)」

「発生していないデータ(Hero)」

をわけたことにより、

繰り返しが発生している部分を、eachで回して処理することができるようになりました。

よって、アイテムが1つ増えてもDBやソースコードを修正する必要はありません。

おまけ

以下のようなときは正規化する必要はありません。

個数が変わらないことが保証されているデータ

例えば、人間のパーツをDBで管理することを例として考えてみましょうか。

人間は肺という臓器を2個持っています。腎臓も然り。

この場合、よほどのことがない限り3個以上にはならないので、

別テーブルに分ける必要はありません。

…変な例で失礼しました。