2009年05月14日
今日のCouchDB
今日も今日とて、CouchDBに弄ばれ続けていたりします。今日は明細の話です。
メインフレーム時代とはCOBOL時代と言い換えてもいいでしょう(RPGやPL/Iの方には申し訳ない)。これは更に言い換えると固定長のパラダイムだとも言えます。オープンシステム時代になりRDBMSが主流となったときに一番のパラダイムシフトは、実は明細の扱い方だったと言えます。
COBOLをご存じの方はOCCURSを当然使っていたはずです。OCCURSとは繰り返しを表すもので、売上データというファイルがあったときに明細部分は繰り返しになりますから、OCCURSを指定します。ややこしい言い回しを使っていますが、要するに配列定義ということです。
さて、COBOLは固定長のパラダイムだと書きました。実はこのOCCURSで定義される配列は繰り返し数が事前に固定されます。例えばOCCURS 5.と書けば5回繰り返しということです。一応可変長が可能ということになってはいるのですが、多分今でも指定した上限を超えられないはずです。プリアロケーションでファイルを確保する際にレコードサイズが指定されるからです(ACOSだったから?)。ですから「何件あるか不明だけど、とにかく全部放り込む」ということが出来ません。
これの何が不便かというと、最初に上限を10とかにしてて、たまたまある売上の明細が11件になってしまった場合に格納できないということになるのです。そこで売上データのレコードを2件にすることになるわけですが、この2件はふたつ合わせてひとつの売上となるので「対になります」という目印が必要になります。かくして売上番号のような項目に「枝番」というのが導入されていくことになったわけです。
これに対してのRDBMSにおけるひとつの回答が1NF、つまり第一正規形です。要するに「繰り返しの排除」です。どうするかというと、明細テーブルを別に切り出すわけです。これは別にRDBMS固有の問題ではなくて、OOPにおけるクラス設計でも明細部を別クラスに切り出すということをやるケースは多く見られます(inner classにするというのは意外と見ない気がします)。
別テーブルに分けることで上限を気にしなくても済むようになりました。ところが今度は別の問題が出てきました。要するに何をするにも必ずふたつのテーブルを組み合わせる、即ちJOINをしないと売上データを再現できないということになってしまったわけです。
リレーションシップの設定で嫌われる、あるいは胡散臭く感じられるのは、正にこの部分だと言えます。というのは、例えば売上データに対して商品マスタや顧客マスタのようなものとのリレーションシップというのは、COBOLの頃からも普通に別ファイルとして切り出していましたし、ライフサイクルが違うので分かれていることに対して違和感がないのです。一方で、明細というのは、所謂見出し部分とライフサイクルが(基本的には)一致している(そうでないこともあるのだけど)ので、扱いが面倒に感じられてしまうのです。しかしRDBMSではOracleのNested tableのような仕組みを持っている場合でないと明細を内包できませんし、その仕組みを利用するにしても、そもそもリレーショナルモデル的に違和感が生じてしまいます。このあたり、実務上・業務上の認識とコンピュータ上のモデルとの乖離がやっかいだったりするのです。
ここで実は結構悩ましい問題にぶち当たります。「そもそも明細とは何ぞや」という問いです。1件ずつの売上をたまたま同じタイミングだからと一束に括った結果、共通部を乱しとして括りだしたので明細という形になるのか、それとも1つの売上というものがあり、それを因数分解していった結果としての明細なのか、というのは、結果として同じモデルになったとしても意味合いは全然変わってくることになります。
…なのですけど、それを数理的・論理的に「これが正解だ!」みたいなのを追求するのは、それはそれで知的興奮もありますし学術的には意味があるのでしょうけど、現地・現物・現場の実情からすると「たまたまこういう書式の伝票があるから使ってるだけ」というのが実際のところだと思うのです。要するに「人が手作業をしやすいように発展した書式を電子化してる」ということです。人はそれほど高尚ではないし、また高尚であるから素晴らしいというものでもないのです。
で、この古来からの習慣化されている形式をひっくり返すのは、労多くして報いが少ないというか、そこに「伝票とは、明細とは」などと大仰に大上段に振りかぶっても、得られる果実は殆どないのが現実です。だったら見た目のとおりにさくっと扱えばそれでいいじゃないか、という気分になってくるわけです。このあたりの私の気分というのは、ERDレッスンで「コード体系はユーザインターフェイスだ」と言い切るのと同根です。ところが実際にはCOBOLだったら固定長だし、RDBMSだったら別テーブルに分けて面倒だし、とかあれこれとあるわけです。もちろん別の言語を持ってくればいいじゃんというのもあるのですけど、それはそれでまた明細云々とは別の課題もあったりするわけですね。
はい、随分と長い前振りでした。ここで「key-value形式だとどうなのよ?」ということになります。RDBMSのメリットのひとつはデータ構造の定義をプログラム側から引っぺがしてデータ側自体に持たせたことにあります。CやCOBOLだとプログラムの側で「このファイルはここからここまでを金額と見なす」というような定義(構造体なりWORKING-STORAGEなり)がある一方で、そのファイルを開いてみても値だけが羅列されているので何がどうなってるのかわかりません。RDBMSだとまずはDDL(create table)でデータ構造の定義をしてそこに値を入れていきますから、プログラムから独立してデータ構造を自明とすることが出来ます。infomation_schemaの価値はここにあるわけです。
一方でkey-valueはキー(属性名)と値の組み合わせで格納します。ですからその値が何を表しているのかはキーによって判別出来ます。一方で、ここからはCouchDBの話に絞り込みますが、ドキュメントとしてひとまとまりになってるので「何となく」ではありますが、それが売上データなのかとかの区別はつけられる可能性があります。これをしっかりとさせようとするなら、明示的に文書型のような属性名のkeyを必ず全部につけるという規約にしておいて、運用でカバーということになります。これはこれで十分にアリだと感じます。スキーマレスでありながら_typeを強要するようではちょっとねぇという気分になるのは自然なことでしょう。その上で可変長のパラダイムですから、例えば明細を配列でネストして持たせるにしても上限などを意識する必要がありません。
というわけで、次のようなデータを作ってみました。
{
"_id": "URI103"
, "_rev": "3101588385"
, "type": "売上伝票"
, "total": 1100
, "得意先_id": "TOK101"
, "伝票番号": 10226
, "details":
[
{ "prod_id": "PRD101"
, "price": "1000"
, "quantity": "1"
, "amount": "1000"
}
, { "prod_id": "PRD102"
, "price": "50"
, "quantity": "2"
, "amount": "100"
}
]
}
これに対して、明細部だけ取り出してくるmapファンクションを作ってみます。こんな感じです。明細(ここでは"details"という属性名)が定義されていれば、その中をぐりぐりと回して1件ずつ切り出すというものです。
function(doc) {
if (doc.type == "売上伝票") {
if (doc.details !== undefined) {
for (var i in doc.details) {
emit(doc.details[i].prod_id, {amount: parseInt(doc.details[i].amount)});
}
}
}
}
これに以前に書いたマッチングというかJOINもどきなやり方を組み合わせると、商品マスタ(といってもそういう見分けがつくドキュメントがあるというだけですけど)とのJOINも出来ますし、emitするものの中に得意先_idとか入れちゃえば普通にあれこれとやれちゃいます。
ただ、こういうのをやってると実感するんですが、やっぱり古式ゆかしい仕様書を書きたくなります。どのViewでどんなmap処理してるのかとか混乱しますし。私がやり方を見つけてないだけなのかもしれないのですが、mapとかreduceのそれぞれの処理でDRYに反するなぁと感じながら同じような処理を何度も書いたりもするので、尚更微妙です。Viewに対するViewが作れない(のかなぁ〜。ほんとに?)ので、コードの再利用性というところで何というか気分はCOBOL時代です(w
でも、この明細の扱いをクリアしたことで、私の中ではこれまでRDBMS使ってきて感じていた「う〜ん」という気分に対して、相当に良い感じで解決策を見いだせそうな気がしています。CouchDBに関するドキュメントでスキーマレスならぬ「スキーマフリー」という言葉が良く使われているのですが、正に自由なスキーマのメリットは非常に大きなものだと感じます。ポテンシャル大きいですよ多分。型とか言ってる時点できっと駄目なんですよ(w この辺はまた改めて書きます。
というわけで他のあれこれもあって、21世紀のネオDOA宣言! みたいなことも言いたいような気分の盛り上がりがあるのですが、あんまり迂闊なことを言って叩かれるのも本意ではない(笑)し、多分私のようにkey-valueと従来とのデータモデリングの比較だとか業務システムへの適用だとかなんてのに興味を持つ人もあんまりいないと思うので、大人しく場末の片隅でkey-value時代のデータモデリングというものを地道に試行錯誤し続けたいと思う次第です。