web-dev-qa-db-ja.com

「SQLサーバーで実行できることをコードで実行しないでください」-これは悪い設計のレシピですか?

一握りの場所で繰り返し聞いたアイデアです。純粋にSQLで問題を解決しようとすると、特定のレベルの複雑さを超えると、コードで処理する必要があることを多少認めます。

アイデアの背後にある論理は、大多数のケースで、データベースエンジンは、コードで行うよりも効率的にタスクを完了する最も効率的な方法を見つけることです。特に、データに対して実行される操作を条件として結果を作成する場合などです。間違いなく、最新のエンジンでは、クエリのコンパイルされたバージョンを効果的にJITして+キャッシュすることは、表面上は理にかなっています。

問題は、このようにデータベースエンジンを活用することが本質的に悪い設計慣行であるかどうか(およびその理由)です。すべてのロジックがデータベース内に存在し、ORMを介してヒットするだけで、線がさらにぼやけます。

209
PhonicUK

素人の言葉で:

これらはSQLが実行するように作られていますであり、信じられないかもしれませんが、コードで行われたのを見てきました。

  • joins-複雑な配列操作が必要なコードワイズ
  • データのフィルタリング(場所)-コードごとにリスト内の項目の挿入と削除を頻繁に行う必要があります
  • 列の選択-コードごとにリストまたは配列の操作が必要になる
  • 集合関数-値と複雑なスイッチケースを保持する配列がコードごとに必要になります
  • 外部キーの整合性-コードごとに挿入する前にクエリが必要であり、誰もアプリの外部でデータを使用しないことを前提としています
  • 主キーの整合性-コードごとに挿入する前にクエリが必要であり、アプリの外部でデータを使用しないことを前提としています

SQLやRDBMSに依存する代わりにこれらのことを行う付加価値のない大量のコードを書くことになります、つまりデバッグおよび保守するコードが増えることを意味します。また、データベースはアプリケーションを介してのみアクセスされると危険に思われます。

325

私はそれを "SQL Serverができることをコードで絶対にしないwell"と言い換えます。

文字列操作、正規表現の動作など、SQL Serverでは実行しません(SQL CLRを除く)。

上記は、結合、集合演算、クエリなどについて話しがちです。その背後にある意図は、(それが得意なことで)SQL Serverに多大な労力を委任し、IO WHERE句を使用してフィルタリングし、それ以外の場合よりもはるかに小さいデータセットを返します)。

123
Oded

SQLサーバーにできることをコードで実行しないでくださいまああなたのために(強調は私のものです)

答えの鍵は、単に何かをするのではなく、SQLが何かをうまくやっているのを探す必要があるということです。 SQLは驚くほど強力な言語です。組み込み関数と組み合わせることで、多くのことが可能になります。ただし、SQLで何かを実行できるという事実は、SQLで実際に実行するための言い訳にはなりません。

決定を下すための私の特定の基準は、返されるデータの量とラウンドトリップの数を調べることです。トリップすると、タスクはサーバーに属します。データ量が同じままであるか、ラウンドトリップ数が同時に減少することなく増加する場合、タスクはコードに属します。

次の例を検討してください。

  • 生年月日を保存し、ユーザーのグループの年齢を計算する必要があります。 SQLサーバーで減算を実行することも、コードで実行することもできます。往復回数は同じままで、あなたに送り返されるデータの量は増えます。したがって、コードベースのソリューションが勝つ
  • 生年月日を保存し、20歳から30歳までのユーザーを検索する必要がある追加のラウンドトリップを必要とせずにデータ量を削減します。したがって、SQLベースのソリューションが優先されます。
47
dasblinkenlight

要するに、それは正しいであることを言うでしょう: "決して実行しないコードベースでのデータベース固有の操作」は、データベースでより適切に処理されるためです。

set base operations の例を見てください。ご存知かもしれませんが、 [〜#〜] rdbms [〜#〜] は、一般的なデータストレージを処理するために構築され、操作操作。

さらに、データベースのプロジェクト選択が重要な役割を果たします。 RDBMS(MS SQL、Oracleなど)を持つことは、RavenDBのようなNoSQLデータベースとは異なります。

22
Yusubov

原則として、DBにはアプリケーションよりも多くの情報があり、一般的なデータ操作をより効率的に実行できます。たとえば、データベースはインデックスを維持しますが、アプリケーションは検索結果にその場でインデックスを付ける必要があります。 それ以外はすべて等しいので、アプリケーションではなくデータベースに作業をプッシュすることで、全体的なワークロードを削減できます。

ただし、製品の規模が大きくなると、通常、dbをスケーリングするよりもアプリをスケーリングする方が簡単になります。大規模なインストールでは、アプリケーションサーバーがデータベースサーバーよりも10倍から1倍以上多いことは珍しくありません。多くの場合、アプリケーションサーバーを追加するには、既存のサーバーを新しいハードウェアに複製するだけです。一方、新しいデータベースサーバーの追加は、ほとんどの場合、劇的に困難です。

したがって、この時点で、マントラはデータベースを保護するになります。データベースの結果をmemcachedにキャッシュするか、アプリケーション側のログに更新をキューイングするか、データを一度フェッチしてアプリで統計を計算することにより、データベースのワークロードを大幅に削減して節約できることがわかりますさらに複雑で壊れやすいDBクラスター構成に頼る必要がなくなります。

13
tylerl

データベースを意図したものに使用しないのは、設計が悪いと思います。良いデータを持つデータベースの外部でルールが適用されたデータベースを見たことがありません。そして、私は何百ものデータベースを見てきました。

したがって、データベースで実行する必要があること:

  • 監査(アプリケーションのみの監査では、データベースに対するすべての変更が追跡されるわけではないため、価値がありません)。

  • デフォルト値、外部キー制約、およびすべてのデータに常に適用する必要があるルールを含むデータの信頼性の制約。すべてのデータが常に変更またはアプリケーションを通じて挿入されるとは限りません。特に、一度に1つのレコードを実行することが実用的でない大規模なデータセットの場合は、1回限りのデータ修正があります(ステータス1として誤ってマークされたこれらの100,000レコードを更新する必要があります)アプリケーションコードのバグが原因で2になるか、またはクライアントAからクライアントBにすべてのレコードを更新してください。

  • JOINSおよびwhere句のフィルタリング(ネットワークを介して送信されるレコードの数を減らすため)

12
HLGEM

「時期尚早な最適化は、コンピュータプログラミングにおけるすべての悪(ほとんどの場合、とにかく)の根源です」-Donald Knuth

データベースはまさにそれです。アプリケーションのデータ層。その仕事は、要求されたデータをアプリケーションに提供し、与えられたデータを保存することです。アプリケーションは、実際にデータを操作するコードを配置する場所です。表示、検証など.

タイトル行の感情は見事であり、ある程度正確です(フィルタリング、投影、グループ化などの重要な要素すべき DBに残されたケースの圧倒的な数)= 「まあ」の順であるかもしれません。 SQL Serverが高レベルのパフォーマンスで実行できるタスクは多数ありますが、SQL Serverが分離された繰り返し可能な方法でSQL Serverが正しく実行できるタスクdemonstrateはほとんどありません。 SQL Management Studioは素晴らしいデータベースですIDE(特に、私がTOADのように使用した他のオプションを考えると)には制限があります。最初に、使用するほとんどすべてのものに制限があります。 do(またはその下のDBで実行する任意の手続き型コード)は、定義上、「副作用」(プロセスのメモリ空間のドメイン外にある状態を変更する)です。さらに、SQL Server内の手続き型コードは、マネージコードがカバレッジメトリックとパス分析を使用して測定できる最新のIDEとツール(この特定のifステートメントがテストX、Y、Zで検出され、テストXが条件を作成するように設計されていることを示すことができます) YとZが「else」を実行する間、trueとその半分を実行します。これは、特定の開始状態でデータベースをセットアップし、何らかのアクションを通じてデータベースプロシージャコードを実行し、予期される結果。

これらすべては、ほとんどのデータアクセスレイヤーによって提供されるソリューションよりもはるかに困難で複雑です。データレイヤー(さらに言えば、DAL)は、正しい入力が与えられたときにジョブを実行する方法を知っていると想定し、コードが正しい入力を提供することをテストします。 SPやトリガーなどの手続き型コードをDBから除外し、代わりにこれらのタイプのことをアプリケーションコードで実行することにより、アプリケーションコードの実行がはるかに容易になります。

6
KeithS

コードの品質への影響に関係なく、SQLサーバー上ですべての処理を実行することが必ずしも良いとは限らないということは、人々が気付いていないように思えます。

たとえば、一部のデータを取得して、そのデータから何かを計算し、そのデータをデータベースに格納する必要がある場合です。 2つの選択肢があります。

  • データをアプリケーションに取り込み、アプリケーション内で計算してから、データをデータベースに送り返します
  • ストアドプロシージャなどを作成してデータを取得し、データ全体を計算して、SQLサーバーへの1回の呼び出しですべてを保存します。

2番目のソリューションが常に最速であると考えるかもしれませんが、これは間違いなく真実ではありません。 SQLが問題(つまり、正規表現や文字列操作)に適していなくても、無視しています。 SQL CLRや、データベースに強力な言語を持っているようなものがあるとしましょう。往復してデータを取得するのに1秒かかり、データを格納するのに1秒かかり、その後、データ全体の計算に10秒かかる場合。データベースですべてを実行している場合、それは間違っています。

確かに、あなたは2秒を剃ります。ただし、データベースサーバー上の(少なくとも)1つのCPUコアを10秒間10秒間無駄にしたり、Webサーバー上でその時間を無駄にしたりしましたか?

Webサーバーは簡単にスケールアップできます。一方、データベースは非常に高価です(特にSQLデータベース)。ほとんどの場合、Webサーバーも「ステートレス」であり、ロードバランサー以外の構成を追加することなく、思いのままに追加および削除できます。

したがって、操作の2秒を短縮することだけでなく、スケーラビリティについても検討してください。パフォーマンスへの影響が比較的少ない、はるかに安価なWebサーバーリソースを使用できるのに、データベースサーバーリソースなどの高価なリソースを無駄にする理由

5
Earlz

SQLはデータ自体しか処理しないので、私はそれを見てみたいと思います。クエリの外観を決定するビジネスルールは、コードで発生する可能性があります。情報の正規表現または検証は、コードで行う必要があります。 SQLは、テーブルの結合、データのクエリ、クリーンなデータの挿入などを行うために残しておく必要があります。

SQLに渡されるのはクリーンなデータであり、SQLは、それを格納、更新、削除、または何かを取得するために必要な以上のものを知る必要はありません。多くの開発者が、データをビジネスと見なしているために、SQLでビジネスロジックとコーディングをスローしたいと思っているのを見てきました。ロジックをデータから切り離すと、コードがよりクリーンで管理しやすくなります。

でも私の0.02ドルだけです。

4

一般に、コードはビジネスロジックを制御し、DBはロジックフリーのハッシュである必要があることに同意します。しかし、ここにいくつかの対抗点があります:

主キー、外部キー、および必須(nullではない)制約は、コードによって適用できます。制約はビジネスロジックです。それらは、コードが実行できることを複製するため、データベースから除外する必要がありますか?

あなたの管理下にない他の関係者がデータベースに触れていますか?もしそうなら、データの近くで制約を強制するのはいいことです。アクセスをロジックを実装するWebサービスに制限することもできますが、これはあなたが「最初に」そこにいて、他の関係者にサービスの使用を強制する権限があることを前提としています。

ORMはオブジェクトごとに個別の挿入/更新を実行しますか?はいの場合、大規模なデータセットをバッチ処理するときにパフォーマンスに深刻な問題が発生します。集合演算は、進むべき道です。 ORMは、操作を実行できるすべての可能な結合セットを正確にモデル化するのに問題があります。

「レイヤー」をサーバーによる物理的な分割、または論理的な分割と見なしていますか?どのサーバーでもロジックを実行しても、理論的にはまだその論理層に該当します。サーバーを排他的に分割するのではなく、別のDLLにコンパイルすることで分割を編成できます。これにより、懸念事項の分離を維持しながら、応答時間を劇的に増加させることができます(ただし、スループットが犠牲になります)。スプリットDLLは、後で新しいビルドなしで他のサーバーに移動して、スループットを向上させることができます(応答時間を犠牲にします)。

3
mike30

イディオムは、ビジネスルールを維持すること、データを関係(データと構造、および関係)と一緒に処理することの方が重要です。すべての問題を1か所で解決できるわけではありませんが、手動などを回避するのに役立ちます。これらのものがデータベースレベルで利用可能な場合は、レコードカウンターの維持、関係の整合性の手動維持など。そのため、他の誰かがプログラムを拡張したり、データベースと対話する別のプログラムを作成したりする場合、以前のコードからデータベースの整合性を維持する方法を理解する必要はありません。手動で管理されるレコードカウンターのケースは、他の誰かが同じデータベースと対話する新しいプログラムを作成したい場合に特に適切です。新しく作成されたプログラムがカウンタに対して正確に正しいコードを持っている場合でも、元のプログラムとほぼ同時に実行されている新しいプログラムが破損する可能性があります。レコードを取得し、新しいレコードまたは更新されたレコードを(コードまたは個別のクエリとして)書き込む前に条件をチェックするコードさえありますが、可能であれば、これはしばしば挿入または更新ステートメントで直接実行できます。データの破損が再び発生する可能性があります。データベースエンジンは原子性を保証します。条件付きの更新または挿入クエリは、条件を満たすレコードにのみ影響することが保証されており、外部クエリが更新の途中でデータを変更することはできません。データベースエンジンがより適切に機能する場合にコードが使用される状況は他にもたくさんあります。パフォーマンスではなく、データの整合性がすべてです。

したがって、これは実際には優れた設計イディオムまたは経験則です。破損したデータのあるシステムでは、パフォーマンスはそれほど役に立ちません。

3
Chris

覚えておくべきことがいくつかあります。

  • リレーショナルデータベースは、外部キーを通じて参照整合性を確保する必要があります
  • 1つのデータベースのスケーリングは困難で費用がかかる場合があります。 Webサーバーを追加するだけで、Webサーバーのスケーリングがはるかに簡単になります。 SQLサーバーの機能をさらに追加してみましょう。
  • C#とLINQを使用すると、コードを介して「結合」などを行うことができるため、多くの場合、両方の世界のベストを得ることができます
0
Joe Phillips

前述のように、往復は時間的に非常にコストがかかるため、データベースとの送受信をできるだけ少なくすることが目標です。 SQLステートメントを繰り返し送信することは、特により複雑なクエリでは時間の無駄です。

データベースでストアドプロシージャを使用すると、開発者はAPIのようにデータベースを操作することができ、背面の複雑なスキーマを気にする必要がありません。名前といくつかのパラメーターのみが送信されるため、サーバーに送信されるデータも削減されます。このシナリオでは、ほとんどのビジネスロジックはまだコード内にありますが、SQLの形式ではありません。コードは基本的に、データベースから送信または要求されるものを準備します。

0
Laurent Goderre

「時期尚早の最適化がすべての悪の根源」-ドナルド・クヌース

作業に最適なツールを使用してください。データの整合性のために、これは多くの場合データベースです。高度なビジネスルールの場合、これはJBoss Droolsのようなルールベースのシステムです。データの視覚化の場合、これはレポートフレームワークになります。等.

パフォーマンスの問題がある場合は、後でデータをキャッシュできるかどうか、またはデータベースへの実装が高速になるかどうかを確認する必要があります。一般に、追加のサーバーや追加のクラウドパワーを購入するコストは、追加のメンテナンスコストや追加のバグの影響よりもはるかに低くなります。

0
parasietje