外部キー制約とは
要するにカラムに入れる値を一定の範囲(別テーブルにある何かしらのカラム)のデータから選べという制限を設けることである。
具体的に言うと、usersテーブルとpostsテーブルがあり、postsテーブルにuser_idという投稿したユーザーのidを格納する外部キーのカラムがあったとする。
この場合、postsテーブルのuser_idカラムに格納できるデータをusersテーブルのidカラムのみに限定するということである。
外部キー制約のメリット、デメリット
この項目は不完全な内容なので、時間があるときに直す。
メリット
外部キー制約をする場合はreference型がよく使われるが、このreference型には二つのメリットがある。
インデックスを自動的に貼り付けてくれる。userではなくuser_idというカラム名を作成してくれる。(「外部キー制約の書き方」の部分を参照)
デメリット
外部キー制約を定義すると参照整合性が保証されること。
参照整合性というのは外部キーテーブルのデータへのリンクが無効になるような変更を主キーテーブルのデータに加えることができない、ということ。
つまり、主キーのない外部キーはあってはならない
もっと嚙み砕くと外部キーを残したまま主キーを消そうとするとエラーになるということだ。
デメリットへの対応
has_many(親テーブルにあたる)の部分にdependentオプションを追加
「外部キー制約とは」で引き合いにした例でコードを書くと以下の通りになる。
class User < ApplicationRecord
has_many :posts, dependent: :destroy
end
外部キー制約の書き方
reference型を使わずに外部キー制約を付ける方法もあるが、コードやファイルの量が多くなるので今回はreference型を使わない方法は割愛する。
reference型を使ったコードを書いてみる。
ここでは「外部キー制約とは」で提示した例を使用する。また、Railsのバージョンは5.2.3とする。
class CreatePosts < ActiveRecord::Migration[5.2]
def change
create_table :posts do |t|
t.references :user, foreign_key: true
end
end
end
重要なのは以下の2点だ。
- referece型だけでは外部キー制約にならず、「foreign_key: true」を付けて初めて外部キー制約として成立する。
- カラム名を書くところが「user_id」ではなく、「user」と書かれているがこれで「user_id」にカラム名がなっている。
一応、「add_foreign_key」を使用した書き方も載せる。
class CreatePosts < ActiveRecord::Migration[5.2]
def change
create_table :posts do |t|
t.references :user
end
add_foreign_key :posts, :users
end
end
また、「add_column」では以下のようなコードになる。
class AddColumn < ActiveRecord::Migration[5.2]
def change
add_reference :posts, :user, foreign_key: true
end
end
まとめ
外部キー制約にはメリット・デメリットがあるので使う場合は、よく吟味すべきである。
個人的には、親テーブルの主キーがほとんど削除されないような場合に使うべきではないかと感じた。
また、外部キー制約を使う際には以下のこ2点に気を付けるべきだ。
- インデックスは自動で貼り付けてくれる。
- reference型だけではなく、「foreign_key: true」のコードが必須。
参考
Railsの外部キー制約とreference型について
外部キー制約でハマったので(rails)
外部キー制約とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
主キー制約と外部キー制約バージョンを選択 - SQL Server | Microsoft Docs
Active Record の関連付け - Rails ガイド