関心の分離(責務の分離)の考え方に従えば、SQLはデータ取得・更新に専念するべきであり、アプリケーション的機能(判断や計算など)を持つべきではない、という考え方があります。
筆者も概ね同意するところでありますが、不用意なSQLの実行は処理速度のボトルネックになりやすく、一概に処理できる議論ではないのが難しいところです。この記事にて、詳細を掘り下げて議論してみます。
尚、関心の分離(責務の分離)については、(関連記事)【関心の分離/責務の分離】変更に強いプログラミング実践手法にて詳しく論じています。併せてご参照ください。
◇目次
前提:アプリケーションは「関心の分離」の原則に従って開発するべきということ
標題の提起の根底には、ある程度熟練したプログラマーであれば必ず気を払っている「関心の分離」の考え方があります。
関心の分離の実践においては、プログラム内に存在する個別の関心事(例えば計算をする、条件を判定する、データを取得する・更新するなど)は、お互いに混ざらないよう隔離して実装するべし、とされています。
関心が混ざった実装は、変更時に新たなバグを埋め込む危険性が上がり、また実装時にも副作用を起こしバグの原因になります。品質が高いプログラムを書くにおいて、関心の分離を意識することは必須と言えます。
SQL側での複雑な処理の実装は、設計の混乱を生み変更に弱くなる
SQLはシステム全体の階層でいえばデータベース層に当たります。データベース層の役割は専らデータの取得と更新を行うことであるため、何かを判断したり計算したりといったアプリケーション的機能は含むべきではありません。
仮にSQLにそういったアプリケーション的機能を混入させると、データベース側とアプリケーション側の両方で何らかの判断やロジック計算を行っていることになります。
その状態では、その処理をそこに書く必然性が揺らぐことになり(なぜこの処理をアプリケーション側で/SQL側でやるんだっけ?といった疑問)、設計の混乱をきたすことになります。また、処理の元となる状態も両方に点在することになりますので、仕様変更にも弱いと言えます。
アプリケーションとSQLの「関心の分離」を具体的にコード例で考える
アプリケーションとSQLの関心の分離について、ごく単純な例で考えてみます。
①関心が混じっているコード
まずNG例です。アプリケーション側は1か所から呼び出しますが、SQLの方で条件分岐しています。
この実装では、新しくロジックを追加したいとき、SQL側に入れるかアプリケーション側で入れるかの余計な議論の元になります。また状態保持もアプリケーションとSQLでそれぞれ行うことになり不便です。
アプリケーション側
//引数flagをSQLに渡してデータ取得
var flag = 1;
var resultSet = get_data_from_database(flag);
SQL
--flag変数が1ならtable1、そうでないならtable2からデータを取得する
IF @falg = 1
BEGIN
SELECT id, hoge
FROM table1
END
ELSE
BEGIN
SELECT id, fuga
FROM table2
END
②関心の分離を行ったコード
続いて、きちんと関心の分離を行ったコードです。条件分岐に関してはアプリケーション側で判断するように変更しました。
この変更により、データベース側にロジックを混ぜる余地はなくなりました。
アプリケーション側
//引数flagで条件分岐して呼び出すメソッドを変える
if (flag == 1) {
//table1からデータを取得
var resultSet = get_data_from_database_table1();
} else {
//table2からデータを取得
var resultSet = get_data_from_database_table2();
}
SQL
--SQL側では条件分岐はしない。
SELECT id, hoge
FROM table1
SQL
--SQL側では条件分岐はしない。
SELECT id, fuga
FROM table2
議論:処理効率と「関心の分離」の葛藤(かっとう)について
データベースにクエリを投げる処理は、処理速度のボトルネックになりやすいため、極力回数を削減し、クエリ一発で可能な限り多くの情報を取得するのがセオリーです。
ゆえに、SQLとアプリケーションの分離と処理効率は議論になりやすい論点です。
要は、「SQL側でプログラミングしたほうが処理が早くなる場合はどうするんだ」という議論です。
結論としては、ケースバイケースで考える必要があるということ
原則論としては本記事で記述した通り関心の分離を優先するべきです。他方、その結果、あまりに処理が遅くなり性能問題がでるのであれば原則に固執するのは不適切と言えます。
ケースと度合いの問題であり一概には言えませんが、多少の処理の不効率を犠牲にしてでも、その後の保守・変更可能性を考慮して原則論的に実装する方がベターである場合がほとんどでしょう。
上記の例で紹介したような条件分岐の事例は論外としても、例えばcase句で取得アイテムをスイッチする程度の要件は頻出します。そういったケースにどのように実装するかこそ、議論の余地があるものでしょう。
SQLにロジックを混ぜる前に、アプリケーション側でもう一度検討すべし
筆者の経験的には、致し方なくSQLにロジックを混ぜる判断をした場合でも、その後見直してみると「別にアプリケーション側でも普通に実現可能だったな」と思わせるケースが多いです。
考慮不測でSQLに余計なものを入れることになっていないか、各現場において改めて検討してみたいところです。
関連記事
記事筆者へのお問い合わせ、仕事のご依頼
当社では、IT活用をはじめ、業務効率化やM&A、管理会計など幅広い分野でコンサルティング事業・IT開発事業を行っております。
この記事をご覧になり、もし相談してみたい点などがあれば、ぜひ問い合わせフォームまでご連絡ください。
皆様のご投稿をお待ちしております。