たまたま(7/5現在)ホームフィードに表示されていた「プログラムでチェックしているとしてもDBにも必ず制約をつけよう!」について
ほぼ全面的に間違っています。
パターン1
プログラムでバリデーションして、DBは何もしない。
もしチェックが漏れた場合にはDBに制約がないため、不正なデータが格納されてしまう可能性があります。
このパターンの問題は「プログラムでチェックしててもすり抜ける」場合があることです。
例えばUNIQUE制約をプログラムでチェックする処理を素直に実装したとしましょう。この処理が2つ同時に走ったとすると
プロセスA | プロセスB |
---|---|
SELECT count(*) FROM tbl where name='hoge' →結果0 | |
SELECT count(*) FROM tbl where name='hoge' →結果0 | |
INSERT INTO tbl (name) VALUES ('hoge') | |
INSERT INTO tbl (name) VALUES ('hoge') |
とチェックが漏れたわけではないのに重複するデータが挿入できてしまいます。これを防ぐには適切にトランザクションを用いる必要があります。
非NULL制約は1行のデータの1カラムだけが対象になりますが、一般的にはそうではなく他のカラムや行が関係してくるので、非NULL制約を例に考えると間違えます。
例えば、最小文字数や数値の範囲、取りうる値が限られている場合などのチェックはDBの制約で行うことができないのでプログラムで制御する必要があります。
普通にCHECK制約でできることですが知らないんでしょうか?
パターン2
DBだけで制御する
呼び出し元でチェックする必要がないため、実装が楽になります。
チェックしてどうするか、という視点が全く抜けています。
制約違反が起きたときのDBの応答を元に普通にアプリケーションに求められるであろうエラー処理(例えば入力フォームに「名前は必須です」と表示する)を行おうとすると、実装が楽になるどころか遙かに面倒になります。それをやったとしても、「1つ制約違反を訂正するたびに次の制約違反がでる」という最悪のUIになります。
制約違反が起きたら死ぬ、ぐらいの書き捨てプログラムならよいかもしれませんが・・・
パターン3
プログラムでもDBでも制御する。パターン1と2のハイブリッドです。
不正データの場合にSQLを実行することなくエラーにすることができるので、パターン2と比べてDBの負荷を下げることができます。
またUNIQUE制約で考えてみましょう。
DBでの制約 | プログラムでもチェック | |
---|---|---|
1 | INSERT INTO tbl (name) VALUES ('hoge') | SELECT count(*) FROM tbl where name='hoge' →結果0 |
2 | INSERT INTO tbl (name) VALUES ('hoge') |
検査のためのクエリが必要ですし、チェックの結果結局挿入するなら2回クエリを行うことになります。DBの負荷は下がるどころか上がります。パターン1のところに書いたように、非NULL制約の場合は挿入するデータだけを見ていればいいのでプログラムに閉じてチェックができるため、例外的に「チェックのためのクエリ」が例外的に不要になります。
ところでこれはそもそも負荷の問題ではありません。
何を間違えたか
たいていのアプリケーションでは
- 入力値がアプリケーション仕様に適合しているか
- DB全体にわたってデータの整合性が保たれるか
のどちらチェックも求められるでしょう。そして、それぞれについて適切なエラー処理が必要になります。上に書いたようにプログラムのチェック、DBの制約だけでは両方を満たすことはできません。結局、普通にアプリケーションを実装するならどちらも必要な処理です。通常
- はアプリケーションでチェック
- はDBの制約
で実現します。
負荷の高低や
1回のDBアクセスを減らすことより、実装する量を減らす方が有意義
コードは可能な限り少ないほうが好ましい
という観点で「やるやらない」「どちらがよい」を考えるのは根本的におかしいです。
チェック条件が変わったときにプログラムとDBの両方を修正する必要があり手間がかかります
(どっちでもやるべきもの、という話はさておいて)パターン2のところで書いたように、DBの制約を単なる入力値検証に使用するとプログラムでチェックする時より遙かに面倒になりえます。その時点で「手間」を比較すると不利ですし、チェック条件が変わったときも同様です。
DBの制約で制御できるものはDBの制約だけで制御する
DBの制約で制御できないものはプログラムで制御する
普通のアプリケーションではすべてのデータをDBに保存するわけではあありません。DBが絡まない場合、もっと極端(でもあり得る)には、入力値の中でもDBに保存するものしないものがある場合を考えて見ましょう。これはプログラムでチェック、これはDBでチェックするのでプログラムでチェックしない、とかやるのでしょうか。この点だけ見てもこの記事の主張は非常にナンセンスです。