メモ: 複合主キーを云々いう前に足りなかったこと

先日、データベース設計で、ナチュラルキーの組合せを使った複合主キーと、その代替となるサロゲートキーのどちらを使うか、という話を書きました。

月末までには、改めて考えを整理するつもりでした。しかし、残念ながら、今の自分の知識ではムリそうです。そこで、考えたことを少しずつ出力することにしました。

この問題について考えるべき(だった)こと

前回の記事に足りなかった観点が3つあります。

  1. データベースの論理設計と物理設計を分ける
  2. 複数ある要素の一部にだけ言及していると自覚する
  3. すべては程度問題である

これらを1つずつ考えてみます。

1. 論理設計と物理設計を分ける

まず、データベースの論理設計と物理設計を分けて考える必要がありました。非常に、基本的なことですが・・・

論理設計で、ユニーク制約が必要なキーと、行を一意に特定するキー(候補キー)を特定します。もちろん、候補キーには、複合主キーも含まれるでしょう。

そして、物理設計で、どの候補キーを主キーにするかと、主キー以外の候補キーにユニーク制約をつけることを決めます。

id:akitsukadaさんが、詳しいエントリを書いてくださいました。

ここでいう「ナチュラルキー」は論理設計とか分析モデルのときにデータの意味をはっきりさせるために抽出し、物理設計/実装段階で物理的制約や要件に応じてサロゲートキーをつけるいいと思う/書いた→http://d.hatena.ne.jp/akitsukada/20110714/1310670491

http://b.hatena.ne.jp/akitsukada/20110715#bookmark-50842882

おっしゃるとおりだと思います。

同じことを指摘してくださった id:lizyさん、id:OTTiiさん、ありがとうございました。

論理設計と物理設計を分けて考え、書く時にも注意を払うべきでした。そうしていれば、「物理設計で主キーにサロゲートキーを採用した場合も、(複合の)候補キーにユニーク制約をかける必要がある」点に、言及できたかもしれません。

ユニーク制約について指摘してくださった id:deep_oneさん、id:mmuuishikawaさん、id:kagehiensさん、id:frkw2004さん、id:da-yoshiさん、ありがとうございました。

元記事で参照したDBFluteのドキュメントにも、はっきり書かれています。

ナチュラルキーには別途ユニーク制約を付与する

サロゲートキーと複合主キー | DBFlute

また、渡辺幸三さんの記事も参考になります。

代理キーを使いたければ使えばよい。ただし、論理レベルのデータ要件を保全するために、必要なユニーク制約をテーブルに盛り込むことや、アプリによるユニーク制約項目の更新を禁止するといったメンドクサイ配慮を忘れてはいけない。私が代理キーを滅多に使わないのはそのためだ。

ナチュラルキーを主キーにしてはいけない: 設計者の発言

少なくとも分析段階では、複合キーにもとづくものだろうが何だろうが関数従属性が確実に認識され、対処されなければならない。

「複合キー」と実装用フレームワーク: 設計者の発言
2. 複数ある要素の一部にだけ言及していると自覚する

次に、説明する内容が全体の一部に過ぎない場合には、そのことを明らかにする必要がありました。

たとえば、次の2つのようなことです。

  1. サロゲートキーの導入が、かならずしも業務変更時の影響範囲を小さくするとは限らないこと
  2. 業務変更時の影響範囲を小さくするためのアプローチは、サロゲートキー導入以外にもありうること

前回考察した範囲は、恥ずかしながらこんな感じでした。

論理的には表の4項目すべてのケースがありうるのに、サロゲートキーを導入することで達成される3番を持ち上げ、複合主キーがもたらす1番にダメ出しをしただけでした。

本文でも、複合主キーを使うことを「業務変更時に影響が及ぶ範囲が広くなる」というように、かならずそうなるかのように書いています。

id:frkw2004さんの「サロゲートキーのデメリットにも言及しないと」というご意見はもっともでした。

  • 本番環境とテスト環境などで、DBのインスタンスが異なると、同じ意味を持つ行が別の主キーを持ちうる
  • データ移行の時に考えなければならないことが増える
  • サロゲートキーをつけない場合よりもインデックスが増えるため、更新時の性能が落ちる場合がある

・・・といったサロゲートキーのデメリットに触れたコメントをくださった id:kamataroさん、id:ku__ra__geさん、id:chroQさん、id:sawarabi0130さん、ありがとうございました。

また、2.は、逆の視点です。業務変更時に影響が及ぶ範囲を小さくする方法は、サロゲートキーの導入以外にもあるでしょう。サロゲートキーの導入は、複数あるアプローチのうちの1つです。

たしかに、サロゲートキーを使うことで、複合キーの構成要素や形式が変わっても、主キーを定義し直すことを避けられる可能性があります。

しかし、それは「業務変更時の影響」のごく一部にすぎません。それ以外の課題には、他のアプローチが必要です。

ここで挙げたことは例ですが、全体的にこういった視点が要ると思います。

3. すべては程度問題である

さて、そのような「ビジネスモデルの変更時に影響が及ぶ範囲」は、「大きい/小さい」の2つの値では表せません。程度問題、度合い、グラデーションで表現される評価です。たとえば、「プログラミングができる/できない」、「英語が使える/使えない」などが、単純に二値で表せないのと同じですね。

そして、その着眼点が全体の中でどれくらい大事かは、関連するほかの要素によって、相対的に変化します。

例を挙げてみましょう。

何らかのアーキテクチャの選択によって、地獄のように複雑なSQLの実装を強いられる事態になったとします。(複雑さは程度問題です)

しかし、SQLをすっかり隠蔽して、単純に扱えるようにしてくれる開発ツールを採用できたとします。(どれくらい軽減できるかも、程度問題ですね)

しかも、ツールの扱い方を開発者が学習するのは、とてもカンタンだとします。(学習コストもまた程度問題です)

図にすると次のようになります。

一連の要素によって、「SQL実装が複雑である」という元の問題は、相対的に重要度が下がりました。

# もちろん、ツールの値段、開発の活発度など、結論に影響を与える関連要素は無数にありますが、ここでは単純化しています。

逆の場合も考えてみましょう。

ふたたび、地獄のように複雑なSQLを実装する必要があるケースを想定します。

今回も開発ツールを採用できましたが、複雑さがそれほど抑えられないものだったらどうでしょうか。

また、そのツールは複雑で、開発者が正しく扱うことは難しいとしましょう。

図にすると次のようになります。

ここで、「SQL実装が複雑である」という問題は、先ほどのように縮退しません。大きな問題であり続けています。

そもそも、「SQLの実装が複雑である」状態を例示する必要がある、という問題もありますが・・・それに加えて、ツールやフレームワークなどの開発環境や使用するDBMSの特徴によって、物理設計の結論に補正(制約)が入りうることも、考える必要がありました。

この点を指摘くださった id:sh2さん、ありがとうございます。

なお、これは、ツールやフレームワークの実装に、論理設計が引きずられるということではありません。

これもまた、渡辺さんの記事に、明解な言葉で書かれているとおりだと思います。

避けるべきなのは、個別の実装用フレームワークの枠組みを通して論理設計のあり方を帰納的に把握しようとすることだ。たとえば、HibernateRailsのような複合キーを扱いにくいフレームワークにたまたま馴染んだとしても「複合キーなど使わずともデータ要件はまとめられる」などと考えてはいけない。

「複合キー」と実装用フレームワーク: 設計者の発言

話があちこち飛んでしまいました。ひとまずこのへんで。