If you see this, you must use any unsupported web browser. Or constructing the page.
この表示が見えている場合、描画処理中です。しばらくお待ちください。 長時間見えている場合は、非対応ブラウザで参照しています。

INTER-Mediator Lab. - Room 1

7. マスター参照の組み込み

マスターにあるデータを使う

住所録では名前や電話番号を記録するだけでなく、「友人」「会社関連」などといった分類を入力したいと思うかもしれません。通常、そうした情報を文字列で入れていても役に立つ場合がありますが、常に一定の情報を入れないと意味がない場合もあります。たとえば、売り上げを記録するのに同じ商品であれば、すべて同じ商品名である必要があります。そうしないと、商品ごとの集計が簡単にはできなくなります。

このように、常に決まっているデータを入力したい場合、その決まったデータを単独のテーブルに入力し、そのテーブルの情報を参照して入力をします。こうした決まったデータを入れておくテーブルを「マスターテーブル」と一般には呼ばれています。

住所録をaddressテーブルで作っているとして、分類を記録するconnectionテーブルがデータベースに用意されているとします。idとcnameの2つのフィールドがあり、idフィールドが主キーフィールドです。

idcname
1親戚
2友人
3会社関係

ここで、住所録の各レコードで、種類を記録したいとします。そのような場合、以下のように、addressテーブルに、con_idフィールドを設けて、connectionテーブルのidフィールドと対応づけます。つまり、風下寒子さんは、「会社関係」の友人であることが記録されたわけです。

idpnametelcon_id
1山田一郎0123-456-98761
2風下寒子0123-456-98763
3屋根裏夫0123-456-98761

このような、数字で突き合わせて2つのテーブルの値を合成するのは、一見すると分かりにくく、余分なことをしていると思うかもしれません。con_idというフィールドに直接「会社関係」と入力した方が手っ取り早いと思うかもしれません。

上記のページを実際に作ったサンプルは、「実験環境(別ウインドウに表示)」に用意してあります。実験環境のページのsample7.html/phpをご覧ください。

まず、この方法の利点を説明しましょう。住所録にたくさんのデータが蓄積された後、「親戚」ではなくて「親類」に変更したいとします。上記のようなデータ構造を取っていれば、connectionフィールドのid=1のレコードのcnameフィールドを変更するだけですべて変更が完了します。addressフィールドのいくつかのレコードのあるフィールドに「親戚」という文字列が入っていたら、それらを検索して特定して、不定数のレコードに対する更新を正しく行う必要があり、より多くの作業が発生することになります。このように、表現や情報そのものを別途管理するということで、確実に同一の値を記録管理できるということです。

このような複数のテーブルでデータを管理して突き合わせなどを行う仕組みを持っているのがリレーショナルデータベースの特徴です。マスターテーブルを使う手法はその代表的な利用方法です。

この住所録が、顧客管理的なものだったとします。そして、各顧客に対するコンタクト(訪問、問い合わせ、電話連絡といったもの)を記録したい場合、addressテーブル以外に、コンタクトをいつ誰がどのような方法で行い、どうだったのかを記録するcontactテーブルを作ります。このとき、1人の顧客に対しては複数のコンタクト結果が発生することが想定されます。一方、1回のコンタクトは1顧客だけという運用をします。このような場合、顧客とコンタクトの関係は「1対多」と呼ばれます。contactテーブルには、対応するaddressテーブルのid値を入れるフィールド(たとえば、address_idフィールド)を用意して、そこに対応するレコードのidフィールドの値を入力します。逆に、addressフィールドには対応するcontactテーブルの主キー値を記録するフィールドは設けません。2つ以上かもしれないという数が決まらない問題もさることながら、contactテーブルのaddress_idフィールドの値から組み合わせは特定できることから、それ以上の対応付けのための情報は不要になります。

コピーをする場合もある

なお、ここで、マスターテーブルの値をコピーした方がいいような場合もあります。売り上げをテーブルで記録する場合、商品を別のテーブルで管理するという商品マスターを確保するのは典型的な設計手法です。商品マスター側では、商品名や単価を記録します。売り上げのテーブルでは、商品テーブルの特定のレコードを参照する情報をだけを記録するということができます。商品名や単価は売り上げのテーブルでは記録しないというわけです。

しかしながら、そうなると、あるときある商品の単価が変わった場合、不都合が起きるかもしれません。商品マスターの単価を書き換えれば、過去の売り上げデータはすべて新しい単価で売ったという結果になります。このように、変更があるような情報については、商品マスターの単価の値を、売り上げのテーブルにもつどつどコピーして記録するのが一般的です。それでも「現在の単価」を記録する意味で商品テーブルを別に用意するのは、間違いなく入力できるなどのメリットをもたらすものです。

ポップアップメニューで入力

addressテーブルのcon_idフィールドに入力するためにポップアップメニューを利用したいとします。このとき、addressテーブルのためのコンテキストだけではなく、connectionテーブルのためのコンテキストも必要になります。定義ファイルでは複数のコンテキストを定義でき、それらは並べて記述します。以下のように、区切りに線を入れて、2つのコンテキストが定義されていることを示します。なお、connectionコンテキストについてはkeyの指定はあってもなくてもかまいません。connectionテーブルにクライアントから更新をすることがないので、必要ないのです。

name: address
key: id
-
name: con
view: connection

そして、ページファイルでは次のようなターゲット指定を持った要素が加わります。

<table>
    <thead>
        <tr><th>名前</th><th>電話番号</th><th>分類</th></tr>
    </thead>
    <tbody>
        <tr>
            <td class="IM[address@pname]"></td>
            <td class="IM[address@tel]"></td>
            <td>
                <select class="IM[address@con_id]">
                    <option class="IM[con@id@value|con@cname]"></option>
                </select>
            </td>
        </tr>
    </tbody>
</table>

これで、テーブルには3つ目の「分類」列が追加され、各レコードに対してポップアップメニューが表示されます。すでにconn_idフィールドにデータがあれば、それに対応する選択肢が選択されているはずです。また、ポップアップメニューを選択すれば、その選択結果で、conn_idの値が更新されます。

ページファイルのターゲット指定をあらためて解説をします。まず、addressコンテキストの展開中に、conという別のコンテキストが登場しています。ここでは、まず、TBODY/TRによるエンクロージャー/リピーターが識別されますが、そのとき、さらに内部にSELCT/OPTIONによるエンクロージャー/リピーターが存在しています。リピーター内部のエンクロージャー以下の要素は、データベースの結果を合成するときには特に何もしません。その代わり、データベースから得られたデータを合成した後、さらに内部のエンクロージャー/リピーターの展開に入ります。ここでは、名前と電話番号をTD要素の値に組み込んだ後、conというnameを持つコンテキストを見て、connectionテーブルへアクセスをしてレコードを受け取り、レコードの数だけOPTIONタグを複製して、OPTIONタグ要素内部にconnectionテーブルから得られた結果を合成します。

OPTIONタグ要素では、2つの新しい内容が含まれています。まず、ターゲット指定は、半角の|によって区切る事で1つの要素に複数の指定を入れる事ができ、それぞれの合成が行われます。ターゲット指定では@で区切られた3つ目の項目があります。3つ目の項目は、データベースのデータを設定する先を指定します。

この場合だと、「con@id@value」という指定により、conコンテキスト(connectionテーブル)のidフィールドの値を、OPTIONタグ要素のvalue属性の値にします。続いて「con@cname」により、conコンテキストのcnameフィールドの値がOPTIONタグの値として設定され、その文字列が選択肢として見るようになります。結果として、ポップアップメニュー部分はデータベースから得れた結果を合成すると、次のようなHTMLと同じ結果になります。

<select class="IM[address@conn_id]">
    <option class="IM[con@id@value|con@cname]" value="1">親類</option>
    <option class="IM[con@id@value|con@cname]" value="2" >友人</option>
    <option class="IM[con@id@value|con@cname]" value="3" >会社関係</option>
</select>

ターゲット指定の3つ目の項目では、以下のような仕組みを利用できます。

  • 省略:要素の値。ただし、フォーム要素は種類に応じて適切な属性へ設定
  • 属性名:その属性の値
  • style.スタイル名:指定したスタイルの値(スタイル名はJavaScriptでのプロパティ名)
  • innerHTML:要素のinnerHTML属性にフィールドの値を設定
  • ($を最初に付加):現在の値の中の$をフィールドの値に置き換える
  • (#を最初に付加):現在の値の後にフィールドの値を追加する

学習時間調査

ログインユーザ:|

読み終わったとき、以下のラジオボタンのいずれかを選択して、「読み終わった」ボタンをクリックしてください。

開いてから今まで、このページを、

ほとんどの時間は読む事に使わなかった
あまり時間をかけて読んではいなかった
他のことと半々くらいの時間をかけて読んだ
多くの時間をかけて読んだ
ほとんどの時間を読む事に使った

このページの内容について、自分が感じる理解度は、

ほとんど分からなかった
少しは分かったが半分以下しか分からなかった
半々くらい
すべては理解できないが半分以上は理解した
ほとんど理解できた

←クリックすると、次のページに移動します。

実験のトップページ(Room 1)へ