More info...

2013-12-10

IDの設計についてのさらに突っ込んだ議論

今日も前回に引き続きデータベース設計の話をする。今回の話で一旦データベース設計については筆を置くつもり(ブログ書いてないで原稿書けよ>俺)であるが、その前に話をすっきりさせて置きたいと思う。最後を飾るテーマはIDの設計である。

数字しかないのに意味を含んだID

前回のエントリを見ていただいた方から、次のような構造を持った学籍番号があるというフィードバックを頂いた。
全部数値で"入学年度下2桁"+"学科コード"+"学科内のあいうえお順の順位"
このようなルールで割り当てた学籍番号を、単なる数値として扱うのであれば大きな問題はない。これは数値しか含まれていないので、SQLのデータ型としては単に数値型を使えば良いだろう。だが、学籍番号から入学年度を判断する、あるいは学科を判断するといった用途で使われるのであればやはり適切ではないといえる。リレーショナルモデルの観点だけからではなく、IDとして適切ではない。

一般的に、大学では在学中に学科あるいは学部を移ることが認められている場合があるが、もし転籍してしまった場合には実際の学科と学籍番号が一致しないことになる。もし実際の学科と学籍番号が一致しなかったら、学籍番号から学科を判定するという処理が失敗することになる。学籍番号を振り直すのだろうか?もしそうしてしまったら、その学生の古い学籍番号に紐付いたデータはどうなるだろうか。いずれにしても問題が出てしまうので手詰まりだ。

ここでもやはりIDにはID以外の意味を持たせるべきではないということが言える。

プライベートな情報

ちょっと話が脱線するが、IDに意味を持たせる副作用はデーターベース設計の問題だけにとどまらない。

IDに意味を持たせると、以前高木浩光氏がブログに書かれたように、IDからプライベートな情報を類推することができてしまうという大きな問題にも発展してしまう。ちなみに、先ほどの学籍番号の場合には、それを見ただけで年齢の下限値や5年以上在学してるかどうかが即座に判断できる。些細な情報だと思われるかも知れないが、そのような情報を知られたくないというケースは0ではないだろう。

特に人に対するIDをつけるときは、プライベートな情報を含まないように細心の注意を払うべきであると言える。

意味を持ったIDの起源

よくある話だと思うのだが、もうひとつ面白い例を紹介しよう。

ある受発注システムにおいて、それぞれの伝票にyyyymm-9999というフォーマットのIDを割り当てるというものである。下4ケタは月次でリセットされる。年月と連番が合わさって初めてIDとして機能するので、現場では「年月と連番をそれぞれ独立したカラムにするか」あるいは「それ単体でIDとなるカラムを持つか」という検討がなされたそうだ。

何故こんな面倒なIDの設計が行われるのか。

そのシステムでは、印刷した伝票をファイリングすることになっており、月ごとにファイルを分けたいがためにIDに意味を持たせる設計になっているのである。しかもファイルに抜けがないかどうかを目視で確認するため、IDに抜けがないようにしたいという要件もある。

これは、いわば紙という物理的なメディアによって本来あるべきデータベース設計が歪められている例であると言える。紙にシステムが縛られるというのは実にバカらしいが、未だにITシステムは紙という物理メディアから脱却できないでいるのだ!これはもはや紙の呪いと言っても差し支えないだろう。

数値なのに桁数を気にしなければいけない場合も、その背後には紙という呪縛が存在する場合が多い。「専用の用紙の枠の中にピッタリ印刷する」というのが桁数が必要な理由だったりするからだ。

よく、紙を用いた業務プロセスは効率が悪いと揶揄されるが、紙の弊害は単に業務の効率への悪影響だけでは済まない。紙によってデータベース設計までもが歪められてしまうのである。これからのIT業界、いや社会にとって紙からの脱却というものが大きな課題であると言える。ついでに印鑑というものも早く用済みにして貰いたいところである。

複合キーは果たしてIDなのか

前回のエントリでは、意味を持つIDの例として種別(文字列3桁)番号(数字4桁)というようなフォーマットの商品コードを挙げ、それぞれのパートを種別(文字列)番号(非負の整数)という2つの属性として定義するという方式について紹介した。この方式は、ドメインの設計としては前者よりもずっとマシではあるものの、実は依然として問題が残っている。

それは番号の部分は一体如何なる意味を持つ属性なのかということだ。言葉を変えると、その番号だけの射影をとってできた集合は何を意味する集合なのだろうか?番号という属性の背後にあるドメインは一体如何なるものだろうか?

実は、番号は種別ごとの連番であって、種別という値がなくなれば意味のないものになってしまう。つまり番号は種別に依存していると言える。そのような単独では意味をなさない属性が存在する意義があるのだろうか?意義があるかどうかは議論が分かれるところであるが、ひとまず意義がないということにして考えよう。

ではその場合どのようにドメインを設計するべきだろうか。

皆さんお察しだろうが、答えは種別という属性がなくても番号単体でIDとして機能するようなドメインにすべきということになる。二つの属性がある場合、それらを組み合わせてIDとするのは良くないのである。このような設計の起源は意味を持ったIDであるが、こういった観点からもIDに何らかの意味を含めることは最初から考えるべきではないと言える。

では「複合キーがある場合にはサロゲートキーが必要になる」という結論に至るのだろうか?いや、そうではない。あくまでも必要なのは単体でIDとして機能する属性(≒カラム)である。そのような属性が他に無ければ、IDとして機能する属性を新たに設ける必要が生じるが、それは「ナチュラルキーとして使える属性がなかった」という状況であり、「ナチュラルキーがあるけど新たにサロゲートキーを設ける」というシチュエーションとは明確に意味が異なる。適切なIDの設計が必要なのだ。

IDの本質

IDとは何かということについて再度考察してみよう。

IDとはモノや人にラベルをつけてそれを一意に特定するものであるが、対象のモノ(あるいは人)と、それに対してつけられたラベル、すなわちIDは等価でなければならない。対象のモノからIDが一意に特定できる必要があるし、反対にIDからモノが一意に特定できる必要がある。

次の図は、あるモノの集合に対してラベルを割り当てた様子である。ラベルがIDとして機能するには、モノの集合とラベルの集合の要素がそれぞれ1対1でなければならない。つまり、2つの集合は全単射になっていなければならない。


そのようなラベルだけが真のIDとして機能し得るのだが、多くの場面でIDとして機能する属性が欠如している。欠如している理由をたどると、紙で業務を行うというしがらみに突き当たるかも知れない。

何のIDなのかを表すためのプロトコル

何の意味も含まないIDというのは、データベースに(あるいはリレーションに)格納されている状態であるからそれが何のIDであるかということが分かるのであって、そのメタデータから切り離され数値だけになってしまうと、とたんに意味を失ってしまう。IDから他の情報を取り除くべきという議論と、メタデータがなければIDが意味を失うという議論はパラドックスのように感じるかも知れない。この問題についてどのように対処すれば良いだろうか?

IDに何らかのメタデータを含めるというアイデアは、紙の文化以外にも「何のIDなのかが他のメタデータ無しで分かるようにする」という考えが背景にあると考えられる。つまりIDに対するメタデータをセットにしてしまおうという考え方だ。ここで重要なのは、データベース内ではメタデータがどのように扱われ、そしてどの時点でそれを失ってしまうのかということだ。

データベース内は、IDがリレーション(≒テーブル)に格納されていれば、その属性の要素であるという事実が、何のIDであるかということを表す(=メタデータの)役目を持つことになる。また、アプリケーション内部でもそのIDがどこに格納されているか(どのクラスのどのメンバーなのか等)という情報がメタデータになり得るだろう。

IDからメタデータが失われるのは、IDがアプリケーションの外に出た瞬間であると言える。つまり、複数のアプリケーションでデータを交換するような場合や、人間がそのIDの意味を知る必要がある場合などには、そのIDが何のIDであるかを示すメタデータが別途必要になるわけだ。そういった場合に、どのようにメタデータとIDをセットで表現するか、あるいはその逆のにセットで表現されたものからメタデータとIDへの変換を行うかというプロトコルを決めておくべきである。

そういった意味では、表示用のコンパクトな名称を決めておくのも悪い考えではない。

先ほどの種別(文字列3桁)番号(数字4桁)というようなフォーマットの例では、新たにIDを設けた場合、番号の部分は存在意義を失ってしまった。この属性はもうIDとしては用をなさないが、表示として使う番号という位置づけであれば使用できる。{ID}→{フォーマットされた表示上のID}というFDがあると考えれば、真のIDを持つことで、ID、種別、表示上のIDをすっきりと分けた設計にすることが可能となる。属性の持つ意味を改めて見つめなおしてみるというのもデータベース設計では重要な作業であると言える。

より良いIDを設計するための注意点

より良いIDとは何だろうか。最も重要な基準のひとつとして、IDがずっと変わらず使い続けることができるかということが挙げられると思う。

そのためにまず考慮すべきポイントはIDの寿命だ。そのIDは一体何通りのモノに対してラベルをつけることができるだろうか?ということだ。この点でも、桁数を持ったIDや、(主に先頭部分に)意味を持ったIDは問題がある。冒頭の学籍番号の例では、「大学が100年以上続いた場合」や「学科が100以上に増えた場合」に対応することができないのは明白であろう。基本的には、半永久的に枯渇しないようなIDを選択するのが望ましい。そういった観点では、用いるべきデータ型は数値あるいはUUIDといったものが適切であろう。(UUIDであれば2^122まで格納可能だ。)何の意味も含まないデータ型を表現するという意味でも数値やUUIDなどが望ましい。

もうひとつ重要なのが、対象のモノの単位を何にするかという点だ。先ほどの図に出てきた左側の集合に、何を要素を含めるかと言い換えても良い。商品であれば「品物1個を要素の単位とするのか」「まとめ売りの場合はどうするのか」「組み合わせ商品の場合は」というように、考慮すべき点がたくさん生じてくるだろう。ここで適切な判断を下すためのポイントは、その集合がいったい何を意味するものなのかを明確にするということである。ここを間違ってしまうと、同じドメインのIDが様々な用途で使われたり、複数のドメインの整合性が取れなくなったりしてしまう。IDは元の集合の全単射に過ぎないのだから、元の集合が何であるかを定義することが決定的に重要なのである。

そして、他のシステムとIDの整合性が取れているかという点も極めて重要であるという点は見過ごせない。システムがひとつしかない場合は話は非常にシンプルだ。一旦IDを決めてしまえば(同じ集合に対して2つのID体系を定義しない限り)、同じモノに対するIDが重複することはない。だが、システムが複数ある場合には、どうしてもシステムごとに個別にIDを定義してしまうという事態が生じてしまうだろう。特にシーケンスやAUTO_INCREMENTで番号を割り当てたような場合にはそうなりがちだ。

すべてのIDを複数のシステムで共通化するというのは現実的に不可能である。従って、共通化する必要がありそうなIDについては最初からIDをどう共有するかという運用まで含めて事前に設計しておき、それ以外のものは特に気にせずそのシステムだけで利用可能なID採番するというようなやり方が現実的な落とし所になるだろう。(例に挙げた学籍番号や商品ID、伝票番号の他、契約番号、部品番号、部門ID、SNSやウェブサービスのユーザーIDなどは共通化するべきである可能性が高い。)

IDの共通化にあたっては、先ほど述べたようにメタデータをどう交換するかという点も考慮に入れる必要がある。また、現実的には困難な場合が多いだろうが、同じ意味のメタデータが重複しないように気をつけなければならない。そのようなIDであれば、無意味なサロゲートキーをつけることなく、他のシステムでも使用することができるだろう。

さいごに

IDを如何に設計するかというのは非常に難しいテーマであると思う。だが、IDをどのように設計するかということはシステム設計の根幹に関わる部分である。どういったものがIDとなり、どういった場合に別途IDをつけなければならないのか。その見極めができれば、データベース設計はより強固なものになるだろう。

適切なIDを設計できない理由のひとつとして、紙の呪縛というものを挙げた。紙の文化に縛られた(あるいは呪われた)ままでは、データベース設計まで大きく歪められてしまうことになる。やはり表示とデータは分けて設計するというポリシーが非常に重要である。逆説的に、表示がデータに紛れ込んで居ないかということを見極める上で、紙の呪縛がないかどうかという判断は役に立つかも知れない。

一般的に、設計という作業では選択肢が複数あるのが普通である。どちらも問題がない、あるいはどちらも問題があるが一長一短であるといった場面に遭遇することがあるだろう。どちらの選択肢もそれほど差がないのであれば、後は決断するだけだ。設計とは決断の連続であると。そして、それはデーターベース設計にも当てはまる。

だが、ほとんど差がないような選択肢に遭遇するまでには、数多くの「明らかにどちらかが有利な選択肢」を乗り越えていくことになる。そこで選択を誤ってしまうのが問題なのだ。安易な判断を繰り返していると間違いに気づくことができなくなってしまうだろう。間違いは後になって致命傷になることが多い。明らかに間違った設計は、早目に気付いて修正するべきである。

設計は慎重に。そしてより多くの人がリレーショナルデータベースをより上手く使ってくれることを願うばかりである。

0 件のコメント:

コメントを投稿