【初級編⑧】テーブル正規化の概要とその手順

データベースのテーブル設計をする上で正規化という考え方は非常に重要です。ただ、初心者には少し難しい考え方でもありますので、できる限り分かりやすく説明していきたいと思います。

テーブルの種類

マスタテーブル

これまでは、普通にテーブルを作成して社員番号や年齢などの列を作成していましたが、こういうテーブルのことを「マスタテーブル」(社員マスタ)と呼びます。マスタテーブルとは、例えば商品の売買等の日常業務における取引内容等によってレコードの件数が増加することがなく、主に他テーブルから参照される為に使用するテーブルのことを言います。紙媒体で台帳として作っている場合もあると思います。

一般的な会社だと、各社員には社員番号が割り振られていると思います。社内システムや書類などに記入する際は、社員番号を使用することが多いと思います。それは、社員番号という数字を使って、各社員を識別できるようになっているということなんですね。

社員番号が分かれば、それに紐付く情報として、名前や部署名や年齢等の情報は得られるようになっていると思います。同姓同名の社員がいる場合名前では識別できませんから、どうしても一意の(ユニークな)番号を使う必要があります。

マスターテーブルの参照

この社員マスタのレコードが増えるのは、基本的には新入社員や中途採用が入った場合のみで、例えば営業職のAさんが、仕事を受注したからといってこのAさんのレコードが増えて行くことはありませんよね。取引内容等によってレコードの件数が増加しないというのはこういうことです。

トランザクションテーブル

それに対して、営業職のAさんが仕事を受注した場合、その受注した仕事の内容を記録しておくテーブルが必要になります。また、このテーブルには、新たに受注が増える度にAさんのレコードが追加されていきます。もっと分かりやすい例で言うと、コンビニで買い物した時のレシートのような、売上明細がそれにあたります。

お客さんが買い物をした際に、1品しか買わなかった場合はその売上明細は1件ですし、5品買った場合は明細が5件できることになります。1件作成されるか5件作成されるかは、実際売上が発生してみないと分かりません。この様に取引内容等によってレコードの件数が増加するテーブルのことを「トランザクションテーブル」と呼びます。

テーブルの正規化とは

正規化とは、簡単に言うとテーブルの構造を「冗長なデータを取り除いた」状態にすることをいいます。冗長なデータとは、同じデータが重複していたり、無駄が多い状態のことを言います。正規化には順番があり、全く正規化されていない状態を「非正規形」と言い、そこから順番に「第一正規形」「第二正規形」「第三正規形」と言います。それでは、実際に正規化の例を説明していきます。

テーブルを設計してみよう

それでは、コンビニでの買い物を例にしてテーブル設計をしてみましょう。簡単に考えると次のようなテーブルになるかと思います。

コンビニ売上明細テーブル

どうでしょうか、エクセルで入力しただけなんですが、だいたいこんな感じじゃないでしょうか。ただしこのテーブルには問題があります。

データ内容が冗長である

住所である”東京都千代田区1”は同じ内容が何回も出現していています。エクセルで手入力する際は、オートコンプリートが働くのでまだマシですが、やはり余分な手間が掛かってしまいます。テーブルにレコードを挿入するINSERT文を実行する際も、無駄な処理が発生することになります。

内容が冗長1

これがもし店舗が移転したらどうでしょうか。「Aコンビニの住所」という情報は、ただ一つでいいハズなのに、4レコードも修正(UPDATEの処理が4レコード分実行される)しないといけなくなります。

内容が冗長2

同じデータの繰り返しを取り除く

それでは、冗長なデータを取り除いた形でテーブルを設計してみましょう。店舗に対する住所は1対1でいいので、店舗マスタを新しく作成し、売上明細テーブルから、住所の列を取り除きました。これにより、オレンジの枠で囲った範囲のデータが節約できることになります。

売上明細テーブルにレコードを追加する際、住所を入力しなくて済みますし、店舗の移転があった場合に住所を更新するのも1レコード修正するだけで済みます。

冗長を取り除いたテーブル

関数従属とキー

第一正規形、第二正規形、第三正規形はそれぞれ違いがあります。その違いとなるのが「関数従属」と「キー」という考え方です。

なんだか難しい言葉が出てきましたが、「関数」とは、数学の授業で習った関数と同じような意味で「一方が決まればもう一方がただ一つだけ決まる性質」のことを言い、データベースの場合は「テーブルの2つの項目同士で、1つの項目の値が決まれば、もう一方の項目の値もただ一つ決まる」ことを「関数従属」と言います。

ここで新たに商品マスタを考えてみましょう。各商品に対して、商品名や単価、仕入値、仕入先といった情報を持つマスターテーブルになります。

商品マスタ

「商品コード」が決まれば「商品名」が決まりますし、「単価」も「仕入値」も「仕入先」も決まります。このような関係を「→」で繋いで、「 商品コード → 商品名 」という風に表現します。このように関数従属がある場合の左側(今回は商品コード)のことを「キー(Key)」または「識別子(Identifier)」と言います。

さらにキーは、データベースで実装する際には主キー(Primary Key)と呼ばれます。

主キーの制約と特性

主キーになる為の制約は二つあり、一つは「重複がないこと」で、もう一つは「NOT NULLであること」です。また制約ではありませんが、主キーにする項目は「値が変更されないこと」であるべきです。

なお、キーは正確には「候補キー」と呼ばれます。これはつまり主キーになる”候補”のキーであるということです。テーブルの構造次第では、主キーになり得るキーが複数存在する場合もあり、その中から主として選んだものが主キーになるということです。

主キーになれる項目

もちろんどんな項目でも主キーになれる訳ではありません。「関数従属」ですから、片方の値が決まった時に、もう一方が複数出てきてしまうとダメなんです。社員番号や商品コードを使用するのがその最たる例で、商品名を主キーにすることは可能ですが、基本的にやってはいけません。なぜなら、同じ名称の商品が存在した場合に区別できない(関数従属の関係にならない)からです。

主キーが名称

この状態でボールペン赤が一つ売れた時、単価や仕入値がいくらなのか、また仕入先がどこなのかを特定することができなくなります。それでは困りますよね。

キーを人工的に作成する

関数従属ではない状態だと色々と問題があることは分かったと思いますので、それを回避するにはどうすれば良いかというと、レコードが一意になるようなキーを人工的に作成してやればいいのです。ここでは、商品一つ一つに重複しないように「商品コード」を振るようしましたので、これで同じ名称の商品があったとしても気にする必要はなくなりました。

主キーがコード

複合キーを使用する

上の例では「商品コード」という単独で使用できる人工キーを作成しましたが、ちょっと考え方を変えてみるとします。先ほどの例で何が問題だったかと言うと、「名称が重複してしまう」ことでした。ということは、重複しないようにしてあげればいい訳です、具体的には、「同じ名称の場合は順番に連番を振る」というやり方があります。

主キーがコード

これだと、「商品名」と「連番」を組み合わせたものなら、レコードを一意に特定できるので主キーになることができます。このように「商品名」「連番」単独では主キーになれないが、複数組み合わせると主キーになれるキーのことを「複合キー」と言います。複合キーになっている時の単独のキーそれぞれのことを「部分キー」と言います。

単独の項目で主キーとするか、複合キーにするかはどちらがいいとは一概には言えませんが、個人的には主キーは単独の項目の方が好きです。






正規化を試してみよう

それでは、非正規化の状態から実際に正規化を行っていきましょう。

非正規化のテーブル

コンビニで商品が売れた時の売上明細(レシート)のテーブルを想定して説明を進めて行きます。レシートはこんなイメージです。

レシート

このデータを格納する為のテーブルを設計してみます。初学者だと、もしかするとこういうテーブル構造をイメージしてしまうかもしれません。

レシートテーブルの構造

売れた商品毎に、「商品コード、商品名、単価、数量、金額」の5列があり、商品が5つ売れたから5×5の25列(背景緑の部分)を用意しています。このテーブル、一見よさそうに見えますが列が冗長になっています。

売れる商品の数は常に5個?

一回で売れる商品の数が、常に5個だというのならまだしも、次の売上でもし1個しか売れなかった場合はどうなるでしょうか。画像の「二つ目の商品 ~ 五つ目の商品」までは全てNULLになってしまいます。

レシートテーブルの構造2

例えば、こういうデータをエクセルで加工や集計をしようとしたら、非常にやりづらいですよね?一回の売上で何種類の商品が売れたのかも分かりにくいし、小計や合計を出そうものなら何個Excelの関数を仕込まないといけないか・・・ということになります。

データベースでもそれは同じで、やはり非常に扱いにくくなります。また、もし一回の売上で商品が6個売れた場合は?六つ目の商品として5列追加しますか?だとすると、それまでの行に対して、5列文を全て埋めてあげないとダメですよね。非常に無駄です。

列の冗長を取り除こう ~ 第一正規形 ~

列が冗長であることがテーブル構造としてマズイことだと分かったと思います。なので、まずは列の冗長を取り除きましょう。そうです、第一正規形とは列の冗長を取り除いた構造にすることです。

今回の例で行くと、冗長になっているのはやはり売れた商品毎の、「商品コード、商品名、単価、数量、金額」にあたります。ですので、この5列は商品が一つ売れる度に行方向へ増えて行くようにします。

第一正規形後

第一正規形は、エクセルで作る表のようにするのに近いです。第一正規形のメリットは以下です。

  1. 集計がしやすい(SUMするにしても縦に一列範囲指定するだけ)
  2. 余分なセルが無い(列が増えないので後からセルを埋める必要がない)

部分キーに従属する項目を別テーブルにする ~ 第二正規形 ~

第二正規形は、主キーが複合キーになっていて、その部分キーに関数従属がある場合、これを別テーブルに出すことです。第一正規形の時とは構造が少し違いますが、次のようなテーブルがあるとします。

第二正規形前

これだと、主キーの部分キーである「商品コード」と「商品名」が関数従属になっていますので、これを別テーブルに出します。商品コードが主キーになる商品マスタを作り、この売上テーブルでは商品コードだけを保持し、商品名は無くすことで重複がなくなります。

第二正規形後

部分キーが関数従属の場合、その項目を別テーブルに出すことができました。これが第二正規形です。

キー以外の項目で関数従属があれば別テーブルに出す ~ 第三正規形 ~

第三正規形では、キーではない項目でも関数従属があれば別テーブルに出します。第三正規形になる前はこのような構造です。商品コードと商品名は部分キーだったので既に別テーブルに出しています。(第二正規形)

第三正規形前

しかし、主キーでは無いものの、店舗コードと店舗名が関数従属になっていますので、これも商品マスタと同じ要領で「店舗マスタ」として外出ししましょう。

第三正規形後

正規化の手順は以上です。正規化はデータベースを設計する上で非常に重要な考え方なので是非マスターしましょう。

ただし、どんな場合でも必ず正規化した方が良いという訳ではありません。正規化した場合は、更新系の処理が効率良く行えるようになりますが、反対に参照系の処理は負荷が増えることになります。エクセルで考えてもらうと分かりやすいと思いますが、一つのテーブル(ワークシート)で管理していたものが、複数シートに分かれることになります。更新は一カ所変えるだけで良かったりしますが、参照は見る箇所が増えるので手間が増えますよね?

原則、正規化をすべし。ただ、場合によりあえて正規化しない選択肢もある、という認識でいれば良いと思います。

この記事と関連性の高い記事