どうも、最近夏バテ気味の人です
今日は[「本当にあったやらかしDB設計⑦【ステートフルDB】」]
(https://qiita.com/bouitengineer12/items/325fec42c1c3bb385c64)に続いてびっくりしたことを紹介します
#ファンクションDB
そもそもファンクション(関数)とは…
あるデータを渡すとあるデータを返す処理のことです
同じデータを渡せば、同じ結果が返ってくるという特徴があります
ファンクションを利用する側は実際にどのような処理を行っているかを気にする必要がなく、inputとoutputだけに集中することができるようになります
これは処理をカプセル化(隠して)おり、非常に便利なんです!
ではファンクションDBとは何でしょうか
それは**「DBを作る人がinputとoutputしか気にしていないため、テーブル構造がめちゃくちゃになっている」**ことです
DBを使う人と作る人が同じ視点で物事を考えてはいけません
何故なら、立場が違うからです
###何が悪いの??
RDBの良さとしては、データの不整合を起こさないように設計することができることにあります
しかし、inputとoutputしか考えていないとこのような設計ができなくなります
(最近外部キー使う使わない論争(?)があったりするのですが、これについては後述します)
###問題
ファンクションDBはいくつかの問題を引き起こします
- テーブル構造がめちゃくちゃになる
ファンクションDBを作る人は内部構造を気にしないので、本来RDBが持っている強力な機能を使えなくなります
ファンクションDBの場合はあり得ないようなバグが発生し得ます
正攻法であれば外部キーをはじめとする制約を使ってデータの整合性を保ちますが、ファンクションDBを作る人はそれをやろうとしません
その結果、離れた場所でエラーが発生することになり、その対応に追われることになります
このようなものがアンチパターンと呼ばれています
アンチパターンとは何でしょうか。
それは、問題の解決を意図しながらも、しばしば他の問題を生じさせてしまうような技法を指します。
Bill Karwin 「SQLアンチパターン」より
- inputによるoutputを保証できなくなる
ファンクションはinputされたデータによってoutputのデータが決まる、というものでした
しかし、ファンクションDBはこれすらもまともにできないことがあります
何故ならば、本来DB側でやるべきでは無い責任を押し付けられている可能性があるからです
DBの責任とは何でしょうか
それはデータを保存することと、呼び出しに応じてデータを返すことです
つまり、それ以外のことをしているのであればよく注意する必要があります
僕の知っているファンクションDBでは、データの変換を行っていました
inputされたデータを整形したり、全然関係の無い型にキャストしたり、明らかにアプリケーションでやるべき計算を行っていたり…
先ほどデータの変換と言いましたが、これはデータを生成していることと同じです
DBで不用意にデータを生成することは基本的には良くありません
アプリケーションで生成したデータをDBに保存する、というのが正攻法です
しかし、ファンクションDBではinputに対するoutputしか考えていないため、この正攻法が使えません
更に、複雑な処理を行ってしまうため、本当にあったやらかしDB設計③【ロジカルクエリー】と同じような問題も発生します
###原因
なぜこんなことが起こってしまうのか、考えてみました
- DBの内部構造に興味がない
DBを使う側の人間が内部構造を気にしなくて良い、というのは理にかなっていますが、DBを作る人が内部構造に興味がないとファンクションDBが生まれます
- アプリケーションとDBの関係性がおかしい
アプリケーションで行うのがめんどくさい処理があり、それをDBに押し付けようとしています
その結果、DBが下っ端仕事をメインにしてしまうことになり、秘伝のタレになっていきます
#解決方法
- アプリケーションで行うべき処理はアプリケーションで行う
めんどくさいからと言ってDBに責任を押し付けてはいけませんし、それを受け入れてはいけません
同じ処理をしているとしても、SQLとプログラミング言語では扱いやすさが違います
- DBの設計をする
アプリケーションとDBは別物です
そのため、別の観点で設計してください
特に、データの保存に気を付けてください
#まとめ
- ファンクションを都合よく解釈するな
- アプリケーションがアプリケーションの仕事をしろ
- DB設計を諦めるな
- 変に高度なことをして悦に入るな
どうだったでしょうか
ファンクションは便利で強力ですが、それを現実世界の全てに当てはめようとするとおかしなことになります
ソフトウェアの世界と現実世界は全く同じではありません
(ちょっと続きます)
#おまけ
最近Twitterで外部キー使う使わない論争(?)を見かけることがありました
勿論、外部キー使うという意見が多いという結果が出ているのですが、使わない派にもそれなりに理由がある様でした
- いやいや、データの整合性なんてアプリケーションで担保すりゃえーやん
- 最近の技術は外部キーを超越したからそんな古臭いもんいらんわ
- データ消そうとするときに外部キーのせいでエラー出るから外したったわ
なるほど…
それぞれに反論していこうかなと思います
いやいや、データの整合性なんてアプリケーションで担保すりゃえーやん
その通りです
確かにアプリケーションでデータの整合性が確保されていれば全く問題ありません
しかし、僕が見てきたものは「アプリケーションで完全にデータの整合性が確保されている」と謳っておきながら実際のDBではデータの不整合が発生していました
しかもこれはここ1年くらいの話です
そうなんです、データの整合性をアプリケーションで担保する、というのはかなり骨が折れる作業なんです
もし、アプリケーションでデータの不整合を完全に無くせたとしても、結局それは外部キーの再実装になってしまうんですよね
僕はそれに価値が見いだせないので黙って外部キーを使います
最近の技術は外部キーを超越したからそんな古臭いもんいらんわ
確かに、アプリケーションのコードとして疑似的な外部キーのようなものを使うようなものはありますが、それが外部キーより優秀とは限りません
コードの再利用には無駄なコードを書かなくて済む、という利点の他に、何回も使われているため洗練されたものになっている、という利点もあります
簡単に言うと、より洗練されたもののほうを使うべき、ということです
データ消そうとするときに外部キーのせいでエラー出るから外したったわ
僕たちは異常時にエラーを発生させるためにコードを書いていますよね?
このエラーもその役割を持ったものです
そもそも、外部キーを使っていないとinsert時やupdate時に異常値を検知することができなくなります
するとどうなるかというと、その異常なデータをアプリケーションで使おうとしたときに実行時エラーが発生します
が、運が悪いと実行時エラーすら発生させることなく、異常なデータを正常なデータとして扱ってしまいます
これがデータの不整合です
エラーというのは、おかしなことがあったらすぐに発生させたほうが原因の追究が楽になります
外部キーを使うことによって、より早い段階でデータの不整合を発見し、エラーを発生させることができるようになります
外部キーを使わないと、本来DB側で発生するべきエラーがアプリケーション側で発生するようになり、疎結合とは程遠いい関係になってしまいます
というか、データを消すときの外部キーのエラーは外部キーを設定するときのオプションで回避できます(cascadeとか…)
####結論
- 外部キーの食わず嫌いはやめよう
便利な機能は使うべきです
外部キーはその中でも飛びっきり優秀な機能です
是非使ってみてください
(ErrorとExceptionを混ぜて説明してましたがそこは気にしないで…w)