データベース 第1部 データモデリング 3 データモデリングの手順


 

3 データモデリングの手順

データモデリングには、トップダウンアプローチとボトムアップアプローチがあります。

トップダウンアプローチとは、ユーザーの要件に基づいて、開発するシステムに求められるテーブル(エンティティ)とそれらの関係(リレーションシップ)をまとめた、概念データモデルを作成してから、データ分析を行う方法です。システム開発の全体像を明確にするために、何が重要なのかを見極め、処理すべきデータのあるべき姿を追求します。

ボトムアップアプローチとは、現状の業務に基づいて、そこで扱われるデータを分析することから始める方法です。データ項目の洗い出しや正規化を行ってから、概念データモデルを作成します。

3.1 トップダウンアプローチの手順

トップダウンアプローチの手順は次のとおりです。

  1. エンティティを洗い出す
  2. リレーションシップを定義する
  3. 属性を洗い出す
  4. 洗い出した属性を検証する
  5. 属性から主キーを決める

3.1.1 エンティティを洗い出す

エンティティとは、「企業や業務が関心を持つ長期的に管理対象となり得るもの」であり、「その性質や意味を表す複数のデータ項目を持ち、特定のデータ項目により識別することが可能なもの」です。簡単にいえば、「データベースとして表現すべき対象物」です。

 

(1) 名詞を抜き出す

 

エンティティは、業務分析資料やエンドユーザの説明から名詞を洗い出してみると比較的容易に見つけることができます。

例えば、エンドユーザから次のような説明があったとします。

当社には、約20の部門があり、社員は各部門に配属されています。現在、営業部当社製品購入を検討しているお客様からのお問い合わせに対応しています。購入済みのお客様からのお問い合わせは製品サポート部で対応しています。しかし、購入済みのお客様が新たに異なる商品の購入を検討される場合もあり、問い合わせの窓口を一本化したいと考えています。

ここから、次のような名詞が洗い出されます。

  • 部門
  • 社員
  • 営業部
  • 当社製品
  • 購入
  • お客様
  • お問い合わせ
  • 購入済みのお客様
  • 製品サポート部
  • 商品
  • 問い合わせの窓口

名詞を抜き出すための落とし穴

名詞を洗い出す作業は簡単なはずです。しかし、これがうまくいかず、適切な設計ができないということが多くあります。どうしてうまくできないかというと、ここで自己判断してしまうからです。例えば、「当社製品」と「商品」、「お客様」と「購入済みのお客様」をそれぞれ同じものだと判断して洗い出さないということです。

まずは、このような自己判断を入れずに単純に名詞を洗い出すべきです。 「当社製品」と「商品」という2つの言葉が出てくるということは当社製品以外の製品があるかもしれないということです。この会社がお客様に販売するものは自社で開発した製品だけでなく、他社から仕入れた製品も販売しているのかもしれません。そして、それを社内では「商品」と呼んでいるのかもしれません。その場合は仕入れ先の情報を管理するエンティティも必要となります。

「購入済みのお客様」も同様です。あえて、ここで「購入済み」と強調しているということは、業務上、購入済みではないお客様と区別する必要があるということです。つまり、お客様の属性として購入済みかどうかを区別できるものが必要であることがわかります。

しかし、最初に「当社製品」と「お客様」しか洗いだせなかったら、これらに気付かず、管理すべきデータを見逃してしまうかもしれないのです。


もちろん、エンドユーザも設計者に任せきりにしないで自分たちの要望をしっかり説明すべきですが、それ以上に設計者がエンドユーザの要望を引き出すべきです。 「当社製品」という表現を書きとめ、 「当社製品といわれましたが、それ以外の製品もあるのですか?」と聞けばよいのです。「いいえ、ありません。」といわれれば、「製品」エンティティだけを考えればよいのだし、「はい、他社から仕入れた商品も販売しています」といわれれば、 「商品」エンティティを加えればよいのです。

 

(2) 名詞を分類する

 

エンティティは、業務分析資料やエンドユーザの説明から名詞を洗い出してみると比較的容易に見つけることができます。


名詞をグループに分ける

まず、洗い出した名詞の中から類似した内容のものを見つけてグループを作ります。そうすると、「営業部」、「製品サポート部」、「部門」は、いずれも部門に関する内容を表現したグループであることがわかります。同様に、「お客様」、「購入済みのお客様」はどちらもお客様情報を管理するので、同じグループにできるのではないかと考えることができます。

また、 「当社製品」と「商品」もどちらもお客さまに販売するものなので、同じグループとしました。

 

(3) 独立して持つかを検討する

 

次に、グループ内の各エンティティを独立して持つか、1つにまとまるかを検討します。

「営業部」と「製品サポート部」は、「部門」の1種である( 「部門」 エンティティのオカレンス)と考えられるので、 「部門」 エンティティとします。


同様に、「お客様」、「購入済みのお客様」はどちらも「お客様」エンティティを1つ作って、購入済みかどうかは「購入済み」という属性を持つことで対応できるのではないかと考えることができます。

また、「お問い合わせの窓口」 はそれだけでグループとしましたが、「お問い合わせの窓口」となる担当者は、購入済みであろうがなかろうが「お客様」には必ず一人いるので、これも「お客様」エンティティの属性としました。


さらに、「商品」は仕入れ先の情報管理も必要なので、「当社製品」とは管理する属性が大きく異なることも考えられ、 「商品」と「当社製品」をそれぞれ別々のエンティティとして持つ方がよいことが判断できます。


このように、洗い出した名詞の中から、エンティティなのか属性なのか、あるいは一種(オカレンス)なのかに分類していき、エンティティになるものを四角形で表します。

3.1.2 リレーションシップを定義する

(1) エンティティで文章を作る

エンティティを洗い出すことができたら、次はリレーションシップを考えます。リレーションシップは関係のあるエンティティ同士を線で結びます。

リレーションシップを定義する方法は、2つのエンティティ間で文章ができるかどうかが目安となります。つまり、1つのエンティティを主語に、もう一つを目的語にして文章が作れるかどうかを考えてみるのです。

例えば、「社員」エンティティを主語、「部門」エンティティを目的語にして考えてみます。主語ということは、「社員は」や「社員が」となり、目的語ということは「部門に」や「部門を」となります。この2つの言葉で文章を作ると「社員は部門に所属する」という文章になります。

これで、これらのエンティティ間には「所属する」という関係が成り立つことがわかります。

(2) カーディナリティを定義する

次に、カーディナリティ(種類の数)を考えてみます。つまり、「社員はただ一つの部門に所属する」のか、「社員は多数の部門に所属する」のかです。そして、その結果を目的語の近くに表現します。「ただ一つ」なら縦線()、「多数」ならカラスの足(クロウズ・フット)のような記号()を使います。

ここでは、「社員はただ一つの部門に所属する」ので、「部門」側は縦線()になります。

同様に、「部門にはただ一人の社員が所属する」のか「部門には複数の社員が所属する」のかを表現します。ここでは、「部門には複数の社員が所属する」ので、「社員」側はカラスの足()になります。

(3) オプショナリティを定義する

ER図がだんだんできてきましたが、まだ、完成したわけではありません。カーディナリティを定義したER図に、それらのエンティティが「任意」か「必須」かを表現しなければなりません。これが、オプショナリティです。

オプショナリティを考えるときは、エンティティ内のデータの数を考えます。

例えば、「部門」には所属している社員が必ず一人でもいなければならないのか、一人もいなくてもよいのかです。通常は所属する社員が一人もいない部門は考えられませんが、もし、必ず一人はいなければならないとすると、所属していた社員が全員一斉に会社を辞めた場合、部門そのものも存在できなくなり、削除されなければならなくなります。

また、所属する「部門」のない「社員」が存在するかどうかです。例えば、正式な配属の決まる前の新入社員は無所属なのか、総務や人事などに仮配属されているのかです。

ここでは、「社員は、所属する部門は必ずあるが、部門は、所属する社員のいないこともある」とすると、「社員」からみた「部門」は必須であり、 「部門」側は縦棒()になります。逆に、「部門」からみた「社員」は任意であり、「社員」側は丸()になります。つまり、必須ならば1以上なので縦棒() 、任意ならば0以上なので丸()となるわけです。

(4) 多対多のリレーションシップの分解

このようにしてリレーションシップを定義しますが、多対多のリレーションシップが定義されてしまった場合を考えてみましょう。

リレーションシップの説明をしたときに、多対多のリレーションシップは、エンティティを見逃している証拠であるとしました。管理対象を見逃したまま概念設計を終えるわけにはいかないので、必ず多対多を分解して、エンティティを見つけるようにしなければなりません

例えば、「顧客は複数の商品を注文する場合がある。また、商品も複数の顧客に注文される場合がある。」という関係をER図にしてみると図のように多対多の関係になります。

商品は複数の顧客から購入されるし、
顧客は複数の商品を購入をする

しかし、多対多のリレーションシップは、見逃しているエンティティがあるということなので、多対多を分解してエンティティを見つける必要があります。この隠されているエンティティを「交差エンティティ」といいます。「リンクテーブル」と呼ばれたり、「対照表」、「対応表」と呼ばれたりもします。

ステップ1:「交差エンティティ」の定義

まずは、「交差エンティティ」の名前を決めます。分解前の多対多の関係を名前にします。「顧客」と「商品」の関係ならば、たとえば「購入」、「注文」、「販売」などが考えられます。

ステップ2:「交差エンティティ」へのリレーションシップの定義

元のエンティティ側が「1」、「交差エンティティ」側が「多」となるような1対多のリレーションシップを定義します。




多対多のリレーションシップは、洗い出せなかったエンティティがある証拠です。そのため、多対多のリレーションシップがある場合には、もう一度エンティティを洗い出す必要があります。しかし、なぜ「購入」エンティティが洗い出しで見つけられなかったのでしょうか。実は、この「購入」エンティティは、イベント系のエンティティです。イベント系のエンティティは、「顧客」エンティティや「商品」エンティティなどのリソース系のエンティティと異なり、実体が無く見つけるのが難しいのです。

しかし、リソース系のエンティティをきちんと洗い出しておけば、エンティティは見つからなくても、リソース系のエンティティの間に多対多のリレーションシップが存在することになるので、それを分解することで、いずれイベント系のエンティティも洗い出すことができます。

そのためにも、リソース系のエンティティをもれなく洗い出すことと、エンティティ間に適切なリレーションシップを定義することを怠ってはいけないのです。

3.1.3 属性を洗い出す

エンティティとリレーションシップの定義が終われば、次は属性の洗い出しを行います。属性は、テーブルの列に該当します。

基本的に、属性は、業務説明、画面イメージ、帳票イメージなどから発見します。例えば、「お客様」エンティティであれば、「会社名」、「住所」、「電話番号」など、エンティティが保有する情報を挙げていきます。ただし、すべての属性を業務資料やエンドユーザの説明から発見できるわけではありません。エンティティの洗い出しで発見した「購入済み」や「問い合わせの窓口」などのように、画面イメージ、帳票イメージなどには明示されていない場合もあります。







また、属性の名前の付け方において、適切な属性名を定義しないと、みんなが了解しているつもりでも実はまったく異なることをイメージしているかもしれないということは、「2.4 属性」のところで説明しました。

日常の世界には同音異義語がたくさんあります。同じ業務に携わっている人同士なら、「価格」といえば、それが何を指しているのかがわかりあえることもあるでしょう。しかし、システム開発者やデータベース設計者は必ずしもその業種や業務に携わっているわけではないでのす。そのため、暗黙の了解が成り立つとは限らないのです。そのため、だれが見ても同じことをイメージできる属性名を付けなければなりません。

そこで、「主要語(管理対象そのものを表すもの)」、「分類語(データ属性や種類を表すもの)」、「修飾語(主要語と分類語だけではデータ項目が一意にならない場合に、データの内容を特定するもの)」を組み合わせて、だれもが同じものをイメージできる名前を付けるようにします。

また、「コード」、「ID」、「番号」、「No」などは、意味を持って使い分けているのか、その時の気分で命名しているのかが判断することができません。「顧客コード」、「顧客番号」、「顧客No」などが、同じものを示しているようでは、誤解のもととなります。統一した命名規則が必要となります。


このように属性の洗い出しを行いますが、最終的には、ボトムアップアプローチの手順において、属性の重複を排除する正規化を行い、エンティティの属性を決定します。


なお、ボトムアップアプローチについては、「ボトムアップアプローチの手順」で解説します。

3.1.4 洗い出した属性を検証する

属性を洗い出した後には、その属性を次の点に気をつけて検証します。

  • 1つの属性に入る値は1つだけか
  • 他の属性から導き出すことはできないか

(1) 属性が複数の値を持つ場合

例えば、「社員」エンティティに「扶養家族名」という属性があり、扶養家族の名前を入れるようになっていたとします。そして、社員のAさんには、奥さんと子供がいます。ともに扶養家族です。したがって、「扶養家族名」属性には異なる複数の値が入ることになってしまいました。

ようするに、「社員」エンティティで「扶養家族名」属性を管理するべきではなかったのです。「社員」エンティティと1対多のリレーションシップを持つ「扶養家族」エンティティを定義すべきだったのです。

(2) 他の属性から導き出すことができる場合

次に、例えば、「年齢」属性が「社員」エンティティにあったとします。しかし、年齢は今日の日付から生年月日を引き算すれば求めることができます。このように他の属性から導き出すことができる値は導出属性といいます。属性としてエンティティに残す必要はありません。

ただし、他の属性から導き出すのに非常に手間がかかるというような場合は、残しておくことも考え方の一つです。無用にプログラムを複雑にする必要はありません。

3.1.5 属性から主キーを決める

属性を洗い出した後には、その属性を次の点に気をつけて検証します。

  • 識別子を洗い出す
  • 識別子の中から主キーを決める

(1) 識別子を洗い出す

属性の検証が終われば、次は属性の中から識別子を洗い出します。

例えば、「社員」エンティティの識別子はどの属性になるでしょうか。「社員名」は同姓同名の人がいるかもしれないので識別子にはなりません。「社員番号」には一意な値が割り当てられているはずなので、「社員番号」が識別子になります。

そして、もう一つ識別子になるものがあります。それは、「携帯電話番号」です。携帯電話も1台ごとに一意な番号が付けられています。

このように、1つのエンティティにおいてデータを一つひとつ識別できる属性または属性の組み合わせは複数存在することがあります。したがって、これらの識別子を候補キー(主キーの候補となり得るキー)と呼ぶことがあります。

(2) 識別子の中から主キーを決める

識別子が決まれば、その中から主キーを決めます。

「社員番号」が主キーの候補として一番に考えられますが、「携帯電話番号」も主キーになり得るかを考えます。 しかし、「携帯電話番号」も識別子ではあるのですが、主キーにはなりません。なぜなら、「携帯電話番号」にはNULLが存在する可能性があるからです。NULLとは、「値がない(未定)」、「値が不明」という状態を表しています。つまり、携帯電話を持っていない社員には、「携帯電話番号」が未定のままです。しかし、社員ではあるので携帯電話を持っていない社員たちのデータも必要です。ですが、「携帯電話番号」に値がないために、この複数の人たちのデータを限定することができません。

主キーになるためには、値が一意であるというだけでなく、NULLではないという条件も満たさなければいけないのです。

「携帯電話番号」が主キーにならない理由がもう一つあります。主キーになるためには、永続性がなければいけません。今、ある番号の携帯電話を持っている社員がAさんであれば、それは今だけでなく今後もずーとAさんのデータが取り出せなければいけません。しかし、携帯電話の会社を変えて携帯電話番号が変わった様な場合など、「携帯電話番号」が永遠に同じであるという保証がありません。また、Aさんの携帯電話のであった番号を、その後Bさんが得ているかもしれません。この場合だと、AさんではなくBさんのデータが取り出されてしまうことになります。


つまり、識別子の候補が複数存在する場合には、次の条件を満たすものを主キーとします。

  • 一意である
  • NULLを持たない
  • 永続性がある

3.2 ボトムアップアプローチの手順

先ほどの「トップダウンアプローチの手順」では、属性や識別子を洗い出すところまで行いましたが、実務では、リレーションシップを洗い出すところまでを行い、属性の洗い出しは正規化を行いながら進めていくのが一般的です。

トップダウンアプローチの手順は次のとおりです。

  1. データを洗い出し、データ項目を定義する
  2. データの正規化を行う
  3. 概念データモデル(概念ER図)を作成する
  4. 導出項目と重複項目を検討する

トップダウンアプローチでは、主に経営層の人たちと打ち合わせしながら業務分析を行いますが、ボトムアップアプローチではシステムを利用している人たちから、現在使用しているシステムの画面や帳票などを提供してもらい、その中から管理対象となる属性を洗い出していきます。実際には、トップダウンアプローチの成果物である、概念ER図が存在しない状態から、ボトムアップアプローチでモデリングしていきます。

3.2.1 データを洗い出し、データ項目を定義する

例えば、次のような伝票が使用されていて、この伝票をデータモデリングするとします。まずは、この伝票から属性とすべきものを洗い出します。


属性を洗い出した結果は下図のとおりです。

洗い出した属性を見ると、これらの属性は受注データを管理するためのものであることがわかります。「金額」や「合計」は他から計算で求められるので、洗い出しをしませんでした。


ただし、洗い出した属性を持つエンティティがこの受注データしかないとしたら、いろいろと問題があることが分かります。

属性名だけでは、イメージがわきにくいので、サンプルデータを見ながら考えてみます。

サンプルデータを見ると、次のような問題があることが分かります。

  • 新商品が発売されたとしても、受注されて受注データができるまで商品の登録ができません。
  • 顧客番号101の顧客名や住所が変更になった場合、その顧客のすべての受注データを変更する必要があります。
  • 受注番号2302の受注が取り消しになって削除された場合、顧客番号107の受注レコードが他になければ、顧客番号107の顧客情報も失われてしまいます。

このように、受注データだけにすべての属性を持たせてしまうと、データの登録や変更、削除といったデータの維持や管理を行うときに支障が生じやすくなります。


データベースには正しいデータが管理されていなければいけません。これらの問題を解消するためにも正規化を行う必要があります。

3.2.2 データの正規化を行う

属性値の重複や属性ごとの独立性を高めるために、属性をたった一つのエンティティとして扱うのではなく、いくつかに分割する必要があります。このように分割していくことを正規化といいます。

正規化を行うメリットには、次のようなものが挙げられます。

  • データが整理されることで、他システムとの連携や移行などが行いやすくなります。
  • 無駄な重複が削除されることにより、保存に必要なデータ領域の削減になります。
  • 同じデータが何度も登場しないようにするため、変更があった場合の修正が容易になります。

ただし、正規化をしすぎると、いくつかのエンティティをたどっていかないと目的の属性が得られないということがおきます。そのためアクセス速度を重視する場合には、あえて正規化を行わないということもあります。


前に上げた受注データは正規化が行われていない「非正規形」といいます。

正規化は次のような手順で行います。

(1) 第一正規化

第一正規形とは、「繰り返し項目」を持たない状態です。すべての属性を1つのエンティティで管理すると、いくつかの属性が繰り返し記述されてしまうことがあります。この問題を解決するために繰り返し記載される属性を別のエンティティとして構成する必要があります。

第一正規化とは、繰り返し部分の属性を他のエンティティに分離することです。

繰り返し項目とは、主キーに対して異なる複数の値を持つ属性のことです。しかし、属性名を見ただけで、繰り返し項目を発見することは難しいです。そのため、実際の設計では、エンドユーザに伝票や画面イメージだけでなく、いくつかのサンプルデータを提示してもらいます。

サンプルデータでは、「商品番号」、「商品名」、「個数」、「単価」といった属性の値が、「受注番号」2301に対して、 繰り返し存在しています。これらの属性が繰り返し項目です。

そこで、これらの繰り返し項目を図のように取り除いて、新しいエンティティを作成します。なお、主キーとなる属性には下線を引きました。

元のエンティティ

これらの繰り返し項目を図のように取り除いて、新しいエンティティを作成する
繰り返しを除いたエンティティ
繰り返し部分のエンティティ
繰り返し部分を除いたエンティティの主キーを繰り返し部分のエンティティの識別子として追加する
繰り返しを除いたエンティティ
繰り返し部分のエンティティ
繰り返し部分エンティティの主キーを決める
繰り返しを除いたエンティティ (受注)
繰り返し部分のエンティティ (受注明細)

このとき、リレーションが失われないように注意をしなくてはなりません。

例えば、受注番号を主キーとして加えないと、誰が何を購入したのかがわからなくなります。また、繰り返し部分エンティティも主キーによって一意にデータを参照できなくてはなりません。よって、「受注番号」と「商品番号」の組み合わせが主キーとなります。

これで繰り返しを持たない第一正規形の完成です。これらの繰り返しを除いたエンティティを「受注」、繰り返し部分エンティティを「受注明細」という名前にします。

(2) 第二正規化

第二正規形とは、「すべての属性が、主キー(主キーが複数の属性から構成されている場合はその全ての項目) に完全に従属している(一意に識別できる)状態」です。

第二正規化とは、主キーの一部に従属する属性を他のエンティティに分離することです。


「受注明細」エンティティの主キーは、「受注番号」と「商品番号」の組み合わせです。また、他の属性としては、「商品名」、「個数」、「単価」があります。

この内、 「商品名」と「単価」は、主キーの一部である「商品番号」だけで一意に決めることができます。つまり、 「商品名」と「単価」は「商品番号」だけに依存しており、 「商品番号」 が同じでさえあれば「受注番号」が異なっていても同じ値になります。

そのため、第二正規化で取り除くことができる属性は「商品番号」 、「商品名」、「単価」の3つとなります。

受注明細エンティティ


主キーの一部に依存している属性を分離する
新しい受注明細エンティティ
新しいエンティティ
新しいエンティティの主キーを決める
商品エンティティ

新しいエンティティの属性を見て、何を管理するものなのかを考え、新しいエンティティのエンティティ名と主キーを決める

ここでは、エンティティ名を「商品」、主キーを「商品番号」とする


商品エンティティの主キー「商品番号」を、主キー依存部分を除いた新しい受注明細エンティティに 識別子として加える
新しい受注明細エンティティ

商品エンティティ


最後に、新しいエンティティ(「商品」エンティティ)の主キーである「商品番号」を、先ほど主キー依存部分を除いた新しい「受注明細」エンティティに識別子として加えて、第二正規形の完成です。



ちなみに、「受注」エンティティの主キーは、「受注番号」だけです。 受注番号」はただ単純に値が一意になるように順番に数値を割り当てているだけなので、主キーの一部に依存した情報はありません。そのため、「受注」エンティティに対しては、第二正規化を行う必要がありません。第一正規化の次に必ず第二正規化を行わなくてはならないのではなく、「受注」エンティティのように、第一正規化は必要でも、第二正規化は必要ない場合もあります。

(3) 第三正規化

第三正規形とは、「すべての属性が主キー以外の属性にも完全に従属している(一意に識別できる)状態」です。

第三正規化とは、主キー以外の属性に従属する属性を他のエンティティに分離することです。

「受注」エンティティの属性である、「顧客名」、「住所」は、主キーである「受注番号」に依存しているわけではなく、識別子ではない「顧客番号」に依存しています。

受注エンティティ


主キー以外の属性に依存している属性を分離する
新しい受注エンティティ

「顧客番号」、「顧客名」、「住所」を第三正規化で取り除く
新しいエンティティ
新しいエンティティの名前と主キーを決める
顧客エンティティ


ここでは、エンティティ名を「顧客」、主キーを「顧客番号」とする
顧客エンティティの主キー「顧客番号」を、主キー以外の属性に依存している部分を
除いた新しい受注エンティティに識別子として加える
新しい受注エンティティ

顧客エンティティ



最後に、新しいエンティティ(「顧客」エンティティ)の主キーである「顧客番号」を、新しい「受注」エンティティに識別子として加えて、第三正規形の完成です。



ちなみに、 「受注明細」 エンティティや「商品」エンティティは、主キー以外の属性に依存する属性がないので、第三正規化の必要はありません。


これで、すべてのエンティティが第三正規形となりました。

顧客エンティティ

商品エンティティ
受注エンティティ

受注明細エンティティ

結果としてサンプルデータは次のようになります。

顧客

商品
受注

受注明細


最初の未正規化のエンティティ構成ではデータの登録や変更、削除といったデータの維持管理を行うときに作業が煩雑になったり、消さなくてもよいデータまで削除されたりと多くの問題がありました。

一方、正規化されたエンティティ構造では、これらの問題は解決されています。データは「1つの事象を一元管理できる」ようになり、データの維持管理が行いやすくなりました。

正規化したことによるメリットには次のようなものがあります。

1. データ管理の容易化
データに変更の必要が生じたときに、変更する手間が大幅に削減されます。
2. データの部品化
正規化されたデータは他のシステムで利用できる可能性が高まります。
3.データ容量の削減
データの関係を明確にすることにより、無駄な列を削除することができます。その結果、データ処理の効率も上がります。

正規化はデータベース設計には欠かせない概念です。しかし、正規化すれば必ず優れたデータベースになるとは限りません。正規化することにより、テーブルの数が増えます。これにより検索などでテーブルを結合する場合は、その処理が複雑になることにより、パフォーマンスが低下する可能性もあります。正規化することにより生ずるメリットと、デメリットのバランスを考えて正規化することが大切です。

とはいっても、正規化によるメリットは、データの関係を明らかにすることにより生じます。ですから、パフォーマンスのことを考えて、正規化を最後までしないということではなく、正規化したものを、状況に応じて崩すという考え方が大切です。

3.2.3 概念データモデル(概念ER図)を作成する

正規化が終わり、管理するエンティティを洗い出すことができました。エンティティを洗い出した後は、リレーションシップを引いて、ER図を書いていきます。

(FK) は、他のエンティティの主キーと関連付く識別子や属性を示す(外部キーと呼ぶ)


第一正規化で、「商品番号」、「商品名」、「単価」、「個数」は識別子である「受注番号」に対して異なる複数の値を持つ属性(繰り返し部分)なので、「受注」エンティティと「受注明細」エンティティに分離しました。そのとき、元のエンティティ「受注」の主キーである「受注番号」と「商品番号」の組み合わせを新しいエンティティ「受注明細」の主キーとしました。

ここで、「受注」エンティティ1つに対して、 「受注明細」エンティティは1つ以上あるわけですから、1対多のリレーションになります。




次に、第二正規化で、「受注明細」エンティティから、主キーの一部である「商品番号」に依存した「商品番号」、「商品名」、「単価」を分離し、「商品」エンティティを作成しました。 「商品」エンティティの主キーは「商品番号」です。

商品は、いろいろな相手にいくつも売れることもあれば、商品によっては一つも売れないこともあります。よって、 「商品」エンティティ1つに対して、「受注明細」エンティティが0以上の1対多のリレーションになります。





最後に、第三正規化で、「受注」エンティティから主キーではない「顧客番号」に依存した「顧客番号」、「顧客名」、「住所」を分離し、「顧客」エンティティを作成しました。 「顧客」エンティティの主キーは「顧客番号」です。

顧客は、何回も注文してくることもあれば、注文を一回もしていないこともあります。よって、 「顧客」エンティティ1つに対して、「受注」エンティティが0以上の1対多のリレーションになります。

3.2.4 導出項目と重複項目を検討する

論理設計の手順で最も重要なテーブル構成の最適化において、次に行うのは、「属性を洗い出す」ところでも少し触れましたが、導出項目と重複項目の定義です。

テーブル構成の最適化を図る上で、さらなる改善を図る場合に、導出項目や重複項目を追加することで負荷を下げることができないかを検討します。

(1) 導出項目

導出項目とは、他のデータ項目から処理や計算により導き出せる項目のことをいいます。この導出項目は、処理で求めることができるわけですから、設計上は必要ない項目という扱いになります。

同じレコードから求められる項目

「年齢」のように、同じレコード中の「生年月日」から求められる導出項目です。こうした項目は、存在しなくてもそれほどの負荷もなく求められるので、特に存在させる必要はありません。

しかし、 何をやるにしても「年齢」を求めることが多いとか、そうでなくても、レコード数が非常に多いということならば、存在する意味はあります。つまり、導出するデータが同一レコード内で求めることができる場合でも、処理のタイミングや処理対象数によっては、追加した方が良いこともあるということです。

別テーブルから求められる項目

「扶養家族数」のように、別テーブル中のレコード件数などから求められる導出項目です。こうした項目は、存在しないと必要となるたびに別のテーブルを検索することになるので、物理的I/Oが増えます。データベース検索のパフォーマンスは「どれだけ物理的I/Oを少なくするか」にかかっています。テーブルの結合をできるだけす少なくすることで、データベースのパフォーマンスは改善されます。

ただし、参照されることはあまりないのに、値は頻繁に変わるというような項目の場合は、値を更新する物理的I/Oばかりが増えて、かえって逆効果となります。

計算以外で求められる項目

導出項目は、計算して求める値以外にもたくさんあります。「商品」テーブルを考えてみましょう。このテーブルでは、商品の価格が変更になる場合も多いので、その履歴も管理しています。「価格改定日」に「単価」の変更があったことを示しています。

それでは、「最新フラグ」という項目がない「商品」テーブルの場合、今日のA4用紙はいくらになるでしょうか?A4用紙は、 2017年5月1日の650円と2018年8月10日の680円があります。

2018年8月10日が最新データなので、現在の単価は680円です。

つまり、現在の単価を求めるだけなのに、このテーブル構造のままでは、最初にA4用紙の最新の「価格改定日」を求め、さらに、その日付のレコードを検索して「単価」を求めるということをしなければならないのです。今後、商品の単価の改定が行われるたびに「商品」テーブルのレコードは増え続け、最新の「価格改定日」を求めるために読み込まなければならないレコードが増えるということです。しかし、どんなにレコードが増えても、実際に必要とするレコードは商品ごとに1件だけです。今のままでは、システムの運用期間が長くなればなるほど、最新の単価を求めるレスポンスが悪くなっていくのです。

そこで、図のように「最新フラグ」という導出項目を追加します。最も新しいレコードに、例えば、Yをいれます。それ以外はNです。こうすれば、今後どんなにレコードが増えたとしても、「商品番号」と「最新フラグ」で検索できることになります。

(最新の「価格改定日」 を求めるには、全レコード検索しなければならないが、 「商品番号」と「最新フラグ」で検索すれば、見つかったところで検索をやめるようにもできる)

(2) 重複項目

他のエンティティの項目と同じ内容を持つ項目のことをいいます。更新時の処理を考えると、複数箇所に存在していないほうが望ましいといえます。ただし、頻繁に参照するとか値を計算するのが複雑だとかいう場合は、明示的に存在させる方がよいかもしれません。

重複項目を持つべき例
受注明細
商品

「商品」テーブルで管理している「単価」を「受注明細」テーブルでも管理する場合、「単価」は2つのテーブルで管理されることになるので、重複項目となります。

「単価」が「受注明細」テーブルにあれば、受注明細金額を計算する際に、「商品」テーブルと結合する必要がなくなるので、検索のレスポンスは向上します。また、「商品」テーブルの「単価」が改定された場合にでも、受注した時点の単価を検索する必要もなくなります。



重複項目を持つべきでない例
受注
顧客

「顧客」テーブルで管理している「顧客名」や「住所」を「受注」テーブルでも管理する場合、「顧客名」や「住所」は2つのテーブルで管理されることになるので、重複項目となります。

「顧客名」や「住所」が「受注」テーブルにあれば、受注一覧を作成する際などに、「顧客」テーブルと結合する必要がなくなるので、検索のレスポンスは向上します。しかし、「顧客名」や「住所」が変更になった場合はどうでしょうか? 「顧客名」や「住所」は、「受注明細」テーブルの「単価」とは異なります。変更前の「顧客名」や「住所」を持っていても意味はありません。 「顧客名」や「住所」が変更になったら、「受注」テーブルの該当するレコードすべてを変更しなければならなくなるのです。

ただし、重複項目も同時に更新しなければならない場合にも、元のテーブルが更新される頻度が少ないのならば、重複項目を持つことも充分に考えられます。つまり、検索のレスポンスが改善され、更新の負荷が少ないのであれば、重複項目を持つ意味は充分にあるのです。