リレーションシップの設定と効果

設定にあたっての注意

 リレーションシップを設定するにあたっては、次の準備をしておくことが重要です。

  • 対応させようとしている双方の列のうち「一」の側の列(一対一の対応の場合は両方の列)に、値が重複してはならないという制約をかけてあること(通常は主キーとすることで制約をかけます)
  • 双方の列のデータ型を一致させていること(ただし数値型とオートナンバー型といった組み合わせで、問題なく設定できる場合があります)

これらができていなくとも、下記のように「線で結ぶ」というところまでは可能ですが、参照整合性等の設定を行うことができず、実質的に意味のある設定ができません。
 また、そもそもなぜそのような準備が必要なのか、つまりは主キーと外部キーの関係がどういうものなのか、そして設定しようとしている2つの列の間に一対多あるいは一対一の関係が成立しているのかどうかを理解できていないと、リレーションシップはうまく活用できません。理解が不十分なまま、同じ列名や同じデータ型の列があるからといってなんとなくリレーションシップを設定しても、たいした意味のない「未定義」リレーションシップを量産することにしかならず、これはむしろ避けるべきです。
 リレーションシップを活用するために必要な予備知識については主キーリレーションシップとは一対多・多対多・一対一の関係などの節を参照してください。

基本的な設定方法

 リレーションシップの設定はテーブルデザインではなく、独自の設定画面で行います。
 まず、ウインドウ上部の「データベースツール」から「リレーションシップ」をクリックします。

 リレーションシップの設定画面が開きます。しかし開いた時点では中身が何もありません。テーブルデザインやクエリデザインの画面ですと何か記入したり選択したりする欄が表示されますが、この画面には本当に何もありませんので、一体何をしていいのかわかりにくいところです。
 そこで、上部にある「テーブルの表示」をクリックします。


 作成されているテーブルの一覧が表示されます。表示させようとするテーブルをクリックして「追加」をクリックしていきます。


 追加されたテーブルは、テーブル名と列名だけに簡略化された形で表示されます。
 必要なだけテーブルを表示させたら「閉じる」をクリックします。何も考えず全部のテーブルを表示させても特に問題はありません。


 テーブルの名前の部分をクリックしてドラッグすればテーブルを移動させることができます。


 いよいよ列どうしの設定に移ります。
 設定しようとする2つの列のうち、一方の列名をクリックし、もう一方の列名に対してドラッグします。この例では、取引先テーブルの取引先コード(主キー)をクリックし、販売テーブルの取引先コード(外部キー)までドラッグしています。


 詳細設定のウインドウが表示されます。詳しくは以下で説明しますので、とりあえず「作成」をクリックします。
 なお、下部にある「リレーションシップの種類」については、列の設定内容に基づき自動的に判定されます。テーブル作成時に意図したとおり、一対多の対応関係とみなされていることがわかります(「一」は取引先テーブルの取引先コードつまり主キーを指し、ひいては取引先テーブルを指します。一方「多」は販売テーブルの取引先コードつまり外部キーを指し、ひいては販売テーブルを指します)。


 2つの列をつなぐような線が出来ました。これがリレーションシップの基本的な設定です。


 さて、リレーションシップを設定したときの主な効果についてです。
 まず、設定の対象となった列は、どちらも(列そのものを)削除することができなくなります。

 次に、設定の対象となったテーブルが1対多の関係にある場合、「一」側のテーブルのサブデータシートとして「多」側のテーブルが自動的に設定されます(サブデータシートを別途指定していない場合)。この例では取引先テーブルが「一」側、販売テーブルが「多」側ですので、取引先テーブルのサブデータシートとして販売テーブルが設定され、取引先ごとの販売の記録を確認することができます。ちなみにテーブルが一対一の関係にある場合は、お互いのテーブルに相手側のテーブルがサブデータシートとして設定されます。


 そして、設定の対象となった両方のテーブルをクエリに置いたとき、設定した列どうしが自動的に結合線で結ばれます。リレーションシップの設定をしなくても一定の条件で自動的に結合線で結ばれますが、右の例のように異なる列名であってもリレーションシップの設定をしておけば自動的に結合線で結ばれます。
 なお、クエリにおける結合線は、「両者の値が一致するという条件(を置いている)」ことを示すものです。リレーションシップの設定における線は(この段階では)「一方がもう一方を参照しています」といった注意書き程度のものであり、下記の参照整合性を設定しない限り実質的な効果も意味合いも薄いのですが、いずれ全く意味が異なりますので注意してください。

参照整合性・連鎖更新・連鎖削除

 さて、リレーションシップの基本的な設定をみてみましたが、単に線をひいただけでは大したメリットはありません。サブデータシートの設定もクエリの結合線もそれぞれの設定画面で設定することができ、別にここでしか出来ないわけではありません。
 むしろ重要なのは主キーと外部キーの間の整合を保証するための以下のオプションです。特に、参照整合性はテーブル間のデータの不整合を防ぎ、複数のテーブルを安全に運用するために欠かせない機能ですので、必ず設定するようにしてください。連鎖更新、連鎖削除については常に望ましい結果をもたらすとは限りませんので、データの性質に応じて判断する必要があります。

参照整合性

 参照整合性を有効にすることにより、主キーと外部キーの間の不整合を防ぐことが出来ます。
 具体的には、主キーとして存在しない値が外部キーの値として記録されてしまうことを防ぐものであり、以下のような操作ができなくなります。

  1. ある主キーの値が外部キーとして存在する(参照されている)とき、その参照されている主キーの値を更新すること。
  2. ある主キーの値が外部キーとして存在する(参照されている)とき、その参照されている主キーの値を削除(行を削除)すること。
  3. 主キーの値として存在しない値が外部キーの値となるように、外部キーを追加(行を追加)すること。
  4. 主キーの値として存在しない値が外部キーの値となるように、外部キーの値の更新すること。

ただし、どういう状況が不整合にあたるのかを理解できれば明らかなことですので、これらを一つ一つ覚える必要はありません。
 なお、ある主キーの値が外部キーとして存在しないのは単に参照されていないだけであり、不整合ではありませんので当然制限もされません。また、外部キーの値の更新については、上記dのように更新後の値が主キーとして存在しないものであれば不可能ですが、更新後の値が主キーとして存在するものであれば可能です。
 きちんとテーブルを正規化したうえでリレーションシップを設定し参照整合性を有効にすれば、その時点でデータの不整合は生じないものと考えられます。これはデータ管理上の1つの理想であり、参照整合性を設定することはとても重要なことといえます。

 実際に参照整合性の設定をしてみます。先ほどの詳細設定のウインドウ(既に線をひいている場合、その線をダブルクリックするとこのウインドウが再度現れます)にて、「参照整合性」のチェックを入れた上で「作成」をクリックします。


 一対多の対応関係を表す「1」と「∞」のマークがそれぞれの列名の横に表示されます。上記の「線で結んだ」だけの状態だとどちらが一でどちらが多なのか(いわば親でどちらが子なのか)わかりませんでしたが、これによりテーブル間の対応関係をただちに見て取ることが出来ます。


 テーブルを開いてデータを入力してみます。販売テーブルに新しい行を追加し、取引先コード(外部キー)として4という値を記録しようとしましたが、4という値は取引先テーブルの取引先コード(主キー)として存在ませんのでエラーとなりました(上記cの制約に反したということです)。平たく言えば「そんな取引先は存在しない」ということです。テーブル間の不整合を防ぐための制約がきちんと機能していることがわかります。


連鎖更新

 さて、先ほどのウインドウに戻ります。「フィールドの連鎖更新」にチェックを入れることにより、上記aの制約が取り除かれ、常に主キーの値を更新することができるようになります。更新すると同時に、その主キーの値を参照している外部キーの値も同じ値に更新され、不整合は生じないという仕組みです。
 なお、連鎖更新を有効にするには、参照整合性を有効にしておく必要があります。


 設定したのち、関連するテーブルを開いてみます。


 ここで、主キーである取引先テーブルの取引先コードの値を更新してみましょう。1行目の山田商事の取引先コードを1から999に更新し、次の行に移ると…


 外部キーである販売テーブルの取引先コードのうち、値が1であったものが一斉に999に更新されます。両テーブルのコードが同じように更新されたため不整合は生じない(それらの販売が、山田商事に対するものであるという事実は保たれている)というわけです。


 さて、それぞれのテーブルの値を元に戻したうえで、今度は外部キー側の値を更新するとどうなるでしょうか。
 このとき、主キー側の値が同時に更新されることはありません。外部キーである販売テーブルの取引先コードの値を1から999に更新しようとしましたが、主キーである取引先テーブルの取引先コードは更新されませんし、同じテーブルにある別の取引先コードも更新されず、単にエラーとなります。やはり「そんな取引先は存在しない」ということになるのです。


 しかし、1から2に変更してもエラーにはなりません。主キーに2という値が存在しているからです。その販売は(山田商事ではなく)斉藤商店に対するものである、と変更したことになるのです。


 慣れないうちはこうした挙動は複雑に感じられるかもしれません。しかし、この設定により主キーの値の変更に伴うテーブル間の不整合の発生を防止できます。これも大変重要で便利な機能です。
 ただし、Access内部はともかくとして、現実世界において主キーの値が広く利用されていることもありますので(たとえば社員コードを社員証に記載していたり、部屋番号が部屋のドアに表示されているなど)、混乱を起こさないためにも、主キーの値の更新を許すべきかどうかについては慎重に判断する必要があります。

連鎖削除

 ふたたび先ほどのウインドウに戻ります。「フィールドの連鎖削除」にチェックを入れることにより、上記bの制約が取り除かれ、常に主キー側の行を削除することができるようになります。削除すると同時に、その主キーの値を参照している外部キーの行も削除され、不整合は生じないという仕組みです。
 なお、連鎖削除を有効にするには、参照整合性を有効にしておく必要があります。連鎖更新は有効にしておく必要はありませんが、併用することは出来ます。


 設定したのち、関連するテーブルを開き、主キー側の行(レコード)のうち主キーが1のもの、つまり山田商事に関する行を削除してみます。


 この主キーの値を参照している外部キーが存在することを示すメッセージが現れますが、構わず「はい」をクリックします。


 外部キー側のレコードが「#Deleted」という値になりました。この時点でこれらのレコードは削除されており、存在していません。


 販売テーブルを開きなおしてみますと、先ほど「#Deleted」となっていた行は現れません。山田商事に関する記録も、それに対する販売記録も一斉に削除されたことがわかります。


 連鎖削除の使い方には注意が必要です。
 上記の例のように、取引先の記録を削除した際に関連する販売記録を同時に削除する、というのはデータの整合を保つという意味では適当かもしません。しかし、全体として問題がないといえるでしょうか。確かに、ある取引先が廃業したり、今後の取引の見込みがなくなったのでその取引先の記録を削除しよう、と考えることがあるかもしれませんが、それと同時に販売記録まで削除してしまうと、売り上げの状況を集計した際に事実と異なる結果となることが考えられます。このような例では、販売記録を誤って削除することを防ぐために、連鎖削除の設定を行わない方が適当と考えられます。
 それでも一連のレコードを全て削除したい場合は、連鎖削除を設定しなくとも、外部キー側のレコードを先に削除し、その次に主キー側のレコードを削除すれば問題ありません。

 では、連鎖削除を設定しても問題ないリレーションシップはどういうものかというと、上記のテーブルの例でいえば販売テーブルと販売明細テーブルの間のリレーションシップが挙げられます。


 設定後の様子です。取引先テーブル、販売テーブル、販売明細テーブルの関係が親、子、孫の関係にあること、つまり1つの取引先に対し複数の販売が行われ、1つの販売には複数の明細(商品)が含まれる、という関係がここから見て取れます。


 さて、販売テーブルにある販売記録のうち1行(販売IDが1であるもの)を削除してみます(このあと確認メッセージがあらわれますがもちろん「はい」とします)。


 すると、それに対応している販売明細テーブルの行も同時に削除されました。
 さて、やっていることは上記の取引先テーブルと販売テーブルの例と同じように見えますが、この場合の連鎖削除は特に問題ありません。販売テーブルにある行を削除するというのは販売の事実をなかったことにするものであり、その内訳に過ぎない販売明細テーブルの行を残しておく必要性はないからです。



 ちなみに商品テーブルと販売明細テーブルの間のリレーションシップについては、やはり連鎖削除の設定は避けたほうが良いと考えられます。商品情報(商品テーブルの行)が削除されたときに、販売明細テーブルから当該商品の販売に関する記録が失われる恐れがあるからです。


結合の種類

 各テーブルをクエリ上で結合する際の結合の種類(内部結合または外部結合)を設定することができます。
 ただし、クエリデザインで変更することもできますので、あまり重要ではありません。
 また、内部結合及び外部結合の詳細については別の節で説明しますのでそちらを参照してください。

 さて、商品テーブルと販売明細テーブルの間の線をダブルクリックして詳細設定のウインドウを出します。ここで、「結合の種類」をクリックしてみます。


 すると、「結合のプロパティ」というウインドウが現れます。デフォルトは「1:両方のテーブルの結合フィールドが同じ行だけを含める」(内部結合)ですが、ここでは「2:'商品テーブル'の全レコードと'販売明細テーブル'の同じ結合テーブルのレコードだけを含める」(外部結合)を選択してみます。


 そして元の画面に戻ると、リレーションシップの線に小さい矢印ができていることがわかります。


 具体的な効果は何かというと、クエリでこれらのテーブルを表示したときに、自動的に外部結合での結合となります。
 実際にクエリデザインの画面を開き、商品テーブルと販売明細テーブルを表示すると、ここでも小さい矢印が表示されているのがわかります。これが外部結合の印です。


 データシートビューで見ると、販売明細テーブルに記録のない商品(食パン)についてもデータが抽出されており、外部結合となっていることがわかります。

 繰り返しになりますが、内部結合、外部結合の設定はクエリデザインで自由に設定できますし、外部結合をデフォルトとして設定する必然性も高くありませんので、参照整合性などと比べると設定する必要性は低いです。