外部キーの参照先レコードが必ず存在することを保証する制約。
参照整合性とは、外部キー(=他の表の主キーを指し示す列)の参照先となるレコードが、必ず存在することを保証する制約のことです。「指し示した先が必ず実在する」状態を保ちます。
身近な例で考えると、郵便物のあて先住所に似ています。封筒(注文)にあて先(顧客ID)を書くとき、その住所が実在しなければ届きません。参照整合性は「実在しないあて先は書かせない」「あて先の家を取り壊すなら、その家あての郵便も先に片付ける」というルールにあたります。
上のツールで▶ボタンを押すと、注文(子)の外部キーが顧客(親)を参照する正常な状態から、存在しない親への追加が拒否され、親の削除が制限・連鎖する様子までを順に確認できます。
参照整合性は、外部キー制約(FOREIGN KEY)を設定することでデータベースが自動的に守ってくれます。外部キー制約は、子テーブルのある列が「どの親テーブルの主キーを参照するか」を宣言したものです。
外部キー制約があると、データベースは次のようなチェックを自動で行います。
・追加(INSERT)時:外部キーの値が親に存在しなければ拒否する
・更新(UPDATE)時:外部キーを存在しない値に書き換えようとしたら拒否する
・削除(DELETE)時:参照されている親を消そうとしたら、設定に従って拒否または連鎖させる
この役割の大切さは、「宙に浮いた参照(どの親も指していない外部キー)」を防げる点にあります。たとえば存在しない顧客を指す注文が混ざると、その注文は誰のものか分からなくなります。アプリ側で毎回チェックしなくても、データベースが入口でブロックしてくれるので、データの矛盾を根本から防げます。上のツールのSTEP3で、存在しない顧客への参照が拒否される様子を確認できます。
参照されている親を削除しようとしたときの動作は、外部キー制約の設定オプションで選べます。代表的なのは次の3つです。
・RESTRICT / NO ACTION(制限):参照されている親は削除できない。整合性を最優先する安全な既定動作
・CASCADE(連鎖):親の削除に合わせて、それを参照する子も自動で一緒に削除する
・SET NULL(NULLにする):親を削除し、子の外部キーをNULL(=空)に書き換える
カスケード(CASCADE)は「連鎖して波及する」という意味です。たとえば顧客を退会させるとき、その顧客のカートや通知設定もまとめて消したい場面で便利です。ドミノ倒しのように、親の削除が子へ次々と伝わります。ただし「消えてほしくないデータまで連鎖して消える」危険もあるので、どのオプションを選ぶかは業務ルールに合わせて慎重に決めます。上のツールのSTEP5で、顧客C1とともに注文O1・O3が連鎖削除される様子を確認できます。
参照整合性を理解するうえで、親テーブルと子テーブルの立場の違いをはっきりさせておくことが大切です。
・親テーブル(参照先):主キーを持ち、子から参照される側。例:顧客表
・子テーブル(参照する側):外部キーを持ち、親の主キーを指し示す側。例:注文表
なぜこの親子関係が必要なのかというと、関連するデータを別々の表に分けて管理するためです。同じ顧客の名前を注文表にも繰り返し書くと、顧客名が変わったときに全行を直さないといけません。顧客情報は顧客表の1か所だけに書き、注文表には「顧客IDで指し示す」ことで、データを1か所で管理できます。
身近な例で考えると、電話帳と通話履歴に似ています。電話帳(親)に名前と番号が1件だけ登録されており、通話履歴(子)にはその番号だけ記録されています。番号から電話帳を引けば誰との通話か分かります。外部キーはこの「番号」にあたります。
参照整合性が守られていないと、「宙に浮いた参照」が生まれます。これは、子テーブルの外部キーが指し示す先の親レコードが存在しない状態のことです。
具体的にどう困るかを見てみましょう。
・誰の注文か分からなくなる:C9という顧客が存在しないのに、O2という注文がC9を指している。この注文は誰のものかアプリで調べようとしても答えが出ません
・集計・印刷の際にエラーが起きる:「顧客名+注文の一覧」を作ろうとしたとき、C9の顧客名が取れずにシステムが止まる可能性があります
・デバッグが非常に難しい:宙に浮いたデータは、時間が経つとどこで壊れたか分からなくなります
参照整合性制約は、こうした壊れた状態を「最初から作らせない」仕組みです。アプリのプログラムでチェックするよりも、データベース側でブロックするほうが確実です。なぜなら、複数のアプリやツールからデータを変更する場合でも、データベースが必ず守ってくれるからです。