この記事の要約
主キーとは、ユニーク制約と更新不可制約が担保されていることだった。
主キー = ID と思っていた時期がありました
ORMを利用するなら主キーをIDにしなくてはいけない(というかそれ以外に設定するとソースコードの記述が面倒になってしまう)ので、何も考えずにIDを設定してました。
例↓
商品テーブル
| column | data_type | 
|---|---|
| id | int | 
| product_code | varchar | 
| product_name | varchar | 
| price | decimal | 
商品オプションテーブル
おまけのお茶や、ご飯大盛り(小盛り)を想定
| column | data_type | 
|---|---|
| id | int | 
| product_id | int | 
| product_option_name | varchar | 
| price | decimal | 
主キーと属性フィールド
しかし、主キーは何も「ID」にする必要はありません。
主キーに求められるのは、ユニーク制約と更新不可制約が担保されていることです。
(ユニーク制約と言うのは同じ値が存在しない一意であるという事、更新不可制約と言うのは一度レコードとして保存されると該当のカラムの値は変更されることを許さないという事ですね。)
これらが担保されていると何が嬉しいのかと言うと、主キーが分かれば属性フィールド(主キー以外のカラム)を一意に定められるということです。
この主キーと属性フィールドの関係を関数従属性と言います。
つまり、上記例のテーブルで言うと、product_idとproduct_option_nameが分かればpriceの値が分かるので、商品オプションテーブルのidは、データモデル上は不要ということです。
(product_idとproduct_option_nameの複合主キーを張る形になる)
そしてここで面白いのは、仮に商品オプションテーブルがただ商品オプション名を保存するためだけのテーブルだったとしたら、priceカラムは必要無くなり主キーだけが残るのですが、データモデル的には必要なテーブルには変わらないという点です。
つまり、テーブルの設計を考える切り口は関数従属性ではなく、主キーが表す変数域ということになります。(この事を定義域制約と言います)
サロゲートキーの導入
とは言え、冒頭でも書いたようにORMを利用するなら主キーをIDにすることを迫られます。
例えばLaravelだと、複合主キーにするならtraitを別にインストールする必要があるそうです。
参考:Laravel5.7における複合プライマリキーを持つテーブルへの挿入と更新
こういった代理で導入される主キーのことをサロゲートキーと呼びます。
このサロゲートキー、便利ですが気をつけないとバグを生んでしまいます。
要するにあくまでも主キーの代わりなので、関数従属性を守る必要があるということです。
例えばユニーク制約を破るとしましょう。
そうすると、同じproduct_idとproduct_option_nameの組み合わせがテーブル上に存在してしまい、どちらのレコードを使用すれば良いのか分からなくなってしまいます。
また、更新不可制約を破り、途中で値が変わるようなアプリケーションを組んでしまうと、全く別のデータとなるか上記のユニーク制約を破ることにも繋がってしまいます。
こういった事が起きないようにデータベース・アプリケーションの設計をしないといけないですね。
まとめ
主キーはテーブル設計を考える際、適当に「とりあえずIDにしておいて、、」とか考えてましたが、今回記事を書くにあたってそれではいけないことが分かりました。
