IDリクワイアド(とりあえずID)

すべてのテーブルの主キーをidという名前の属性(列)にし、ドメインを整数にしてしまうというもの。これを擬似キーともよび、本来のキーを自然なキーとよぶ。Railsなどのフレームワークなどが採用している設計指針に対する反論ともいうべきだろうか。本書では、そういうことを一律に強制することをアンチパターンとしている。

なぜアンチパターンなのか

冗長なキーが作成されてしまう

自然なキーとは別に主キーを作るのは冗長だという。
冗長なのはなぜダメなのだろうか?いくつか理由が考えられるが、

  1. スペースが無駄
  2. システムの複雑性が増える

といったところだろうか。スペースが無駄というのはその通り。ただし、速度に影響が出るほどの大きさではない。システムの複雑性が増えるというのは、ID属性で全て統一するのならそうともいえない。Railsでは「設定よりも規約」といっているように、一定のルールに従っていれば、コード量の記述が減り、システムの把握も簡単になる部分がある。ただ、私が問題だと思うのは別にある。詳しくは後に述べる。

重複行を許可してしまう

id列を主キーに設定するとUNIQUE制約がかかるため、自然なキーでればUNIQUE制約がそのまま適用されるが、ID列を主キーにしてしまうと自然なキーの一意性が担保されなくなるという指摘。IDを主キーとした場合は自然なキーをUNIQUE制約とする方法もあるが、その場合はid列はただの無駄だといっている。これは先の冗長であるということと同じ意味だろう。これだけで否定する価値が有るほど大きな問題とも思えない。

キーの意味がわかりにくくなる

idという属性名がすべてのテーブルに存在するので、異なるテーブルのIDの結果を出力するときに困るではないかという指摘。そこで、属性名をIDではなく「テーブル名+ID」という属性名にすればよいという。特に異論はないが、フレームワークを使っている人には関係がない。また、自然なキーを使うべきだという主張と関係はない。

USINGを使用する

上記の指摘と似ている。INNER JOIN句などで使用されるUSINGが使えないという指摘だが、これも「テーブル名+ID」という属性名にすれば解決する。

複合キーは使いにくい

自然なキーを使うと複合キーになり、自然なキーで結合するのが大変だという人がいるが、それを使いにくいというのは、現実を正確に表すことを拒否するようなものだという主張。数学者が2次元や3次元の座標を使うことを拒否し、物体が1次元に存在するかのように計算を行うようなものらしい。
しかし、この主張はなんだか怪しい。1次元のデータに変換しているだけなので、数学的には等価であり、むしろSQLの記述量を減らしているという点では優れている。それだけではなく、フレームワークを使う上ではとても扱いやすい。また、RDBから取得したオブジェクトでプログラミングする場合はID属性が主キーになっているといろいろ共通化ができて便利である。
そもそも、これまで2つの主張がSQLの記述量に対する批判であるのに、今度は立場が逆転すると、記述は増えてもいいというのはどうだろう。単に、どういう場合に記述量を減らしたいか、ということのトレードオフにすぎないと思うのだが。

解決策について

わかりやすい列名

具体的には2つあげている。
一つ目は、IDではなく「テーブル名+ID」にしようという主張。
二つ目は、外部キーの列名も同じように定義し、主キー名が被らないようにしようという主張。
いずれも結合時のSQLの記述量を減らすためのTIPSともいえる。よって、SQLの記述量を減らすためであり、O/Rマッパーを使うときは別だ。

規約に縛られない

id列の強制というRailsの規約に縛られないようもできる。列名の明示的な指定は、実用的な主キー名になる、とのことらしいが、実用的とは何を意味しているのだろうか?もう少しきっちり説明すべきだと思うが、以下の様な事が考えられる。

  1. 自然なキーにしておけばUNIQUE制約が自動でついてくるので便利
  2. 自然なキーであれば外部キーにも自然なキーがくるので、場合によっては結合しなくてもそのまま使える

1は先に述べた。2については次項の最後に記述されていた。たとえば自然なキーであれば、住所録のテーブルに社員コードがくるが、IDであれば、社員IDという内部的な連番が来るので、別途結合が必要になる*1。ただ、SQLで全て記述するならともかく、Railsなどのフレームワークの規約に背くほどの内容ではない。ただし、本当の自然なキーにUNIQUE制約を別途設定することはおすすめしたい。

自然キーと複合キーの活用

IDのような擬似キーを使うという義務はないよ、という内容と、擬似キーは変更に強いよという主張。こういう記述があると、このアンチパターンの主張とは逆に、擬似キーのほうが断然いいようにみえる。つまり、自然キーは変更を受けやすいが、擬似キーはたんなる連番であるがゆえにほぼ変更されることがない。ということである。しかも、この本ではこの問題に対する唯一の解決策だといっているが、そうすると、とりあえず擬似キーを使っておくのが正しいと思えるのでは?
「規約は、役立つと思える場合のみ従いましょう」、と最後に書いているが、この本の読者が、このアンチパターンのタイトルだけを読んで、このアンチパターン自体を規約としてしまうと、とてもひどいことになる。

擬似キーを推進する理由

擬似キーを推進する理由をもう少し考えてみる。IDリクワイアドの設計指針を推奨している別の本がある。

楽々ERDレッスン (CodeZine BOOKS)

楽々ERDレッスン (CodeZine BOOKS)

この本では「自然キーと複合キーの活用」で述べた「変更に強い」ことを推進の大きな根拠にしている。また、実際にありそうな問題を使って説明しているところが面白い。以下、少し取り上げてみる。

コードの洗い替え問題

通常のコードをキーにしていたが、次期から別のコード体系にしたいので洗替したいという場合、コードを完全に入れ替えてしまうと過去のトランザクションデータを参照できなくなる。しかし、もしIDをキーにしていれば、コードを書き換えてもIDは不変なので、問題ない。
私が前回述べた外部的なコードと内部的な実装を分けるべきという考え方に近い。

諸口問題

顧客マスタをわざわざ登録するまでもないような一時的な顧客を入力するために、コードを付けなくていいような顧客を追加できるようにする。継続的な顧客になった時点であらためてコードを設定する。ということができるというもの。これには少し私には疑問がある。*2

  1. 一時的な顧客が多いと顧客マスタが大きくなるにもかかわらず、ほとんどのデータは使用されない。このため、過去データを切り離してバックアップを取るといった措置がやや面倒。
  2. 継続的な顧客になった時点で改めてコードを設定するとあるが、どうやってコードのないデータから顧客を探すのだろうか?名前だと同名や紛らわしい会社と認識を誤ってしまわないか?
  3. 基本的に諸口の顧客は使い回しを想定していないため、同名のデータが大量にできてしまわないか?
計画系コードの扱い

計画系のコードの場合はコード自体にステータスが存在し、コードの変更履歴を持たなければいけなくなる。こういう場合に使えるという指摘。ステータスが明確であれば、ステータスごとに持てば良いという設計方針もあるが、特に関係なく履歴を持つというのであれば、最もよい設計だと思う。トランザクションデータも同様で、自然なキー+履歴Noなんてもつのであれば、IDのみの方がより単純でよい。

擬似キーの問題について

一方、私が擬似キーに対して問題を感じるのは、以下のとおりである。この本ではSQLアンチパターンなので、SQLに関するデメリットの指摘になるのは仕方がないかもしれないが、この問題はSQLなどの実装上のものというよりは純粋に設計上の問題が大きいと思う。この本はRDBにとって、より自然な設計を誘導したいのだろうと思うので、これをアンチパターンとしたい気持ちはわからないでもない。

キーがわかりにくくなる

ひと目でテーブルの何がキーなのかがわからなくなるという点である。IDを主キーにしたERモデルをみてもぱっと見でシステム全体がどういうテーブルの構造になっているのかが分かりにくい。

自然なキーを意識しなくなる

先程は自然なキーをUNIQUEにすればよいとは述べたが、これは同時にしなくてもよいという意味でもある。つまり、自然なキーがなくてもよいので、該当テーブルの定義・性質がはっきりしなくなり、実装上の都合でテーブルが肥大化、あるいは分割してしまうことになりかねない。このため、後から自然なキーを設定したくてもできなくなったり、キーがどんどん追加されるといった問題が生じる。主キーというのは該当テーブルの性質を決定づける重要な要素である。変更が簡単にできるのはとてもいいことであるが、変更しすぎて一体何のテーブルだったのかわからなくなるのは問題である。

私の結論

単純に実装だけを考えれば、むしろリクワイアドIDのほうがいいような気がする。これは、Beautiful Codeの17章「もうひとつの間接参照」(Another level of indirection)という考え方に則っている。IDとはポインタであり、参照だと私は思うからである。

ビューティフルコード (THEORY/IN/PRACTICE)

ビューティフルコード (THEORY/IN/PRACTICE)

英語版wikipediaには、

A famous aphorism of David Wheeler goes: "All problems in computer science can be solved by another level of indirection"
(拙訳)
David Weelereが言う有名な格言にはこうある「コンピュータサイエンスのすべての問題はもう一つの間接参照で解決できる」

とある。
では、変更に強いデータベース設計をするためにはすべてIDを設定しよう、なのだろうか?確かに、変更に強いテーブル設計になる。ただし、変更に強いというのが無条件にいいか?ということをいいたい。それはやはり、先に上げた冗長性・設計上のわかりにくさと引き換えに得ているのであり、無条件になんでもいいというわけではない。つまり、変更が起きそうなテーブルには主キーにIDを使った設計を入れ、そうでないところはそのままでいい。そうすると、設計担当者がIDを入れているということのメッセージが他の人に伝わる。
もちろん、すべての変更を予測することは難しい。ではそのときはどうするか?それは、設計者の裁量で決めるべきだ。置かれたシステムの状況・規模・使用する言語、それらによって全て違うだろう。それを一律何も考えずに規約でIDを主キーに縛るのはおかしい。そういう意味では本書と同じ結論とは言える。
ちなみに、Railsで主キーがIDでない設計は絶対にやるべきではないと思う。それを破ることによるメリットがあるとは思えない。

*1:もちろん、社員ID=社員コードとするという設計があるが、キーに整数の単なる連番を使うというIDリクワイアドの設計指針の意味合いからは異なるので、ここでは違うものとする。また、いわゆる擬似キーを生成する機能はでないので、抜け番がおきやすいが、人間が扱うコードはたいてい抜番を許さないはず。

*2:でも、この章のこの部分は私はとても納得してしまった。"データ中心アプローチを標榜する人たちの殆どが「正しいコード体系を整備しなければならない」と声高に叫びます。しかし、ユーザーが欲しているのは論理的に正しいコード体系ではなく、「実務において便利なコード体系」なのです。"