web-dev-qa-db-ja.com

「アドホックSQL」とストアドプロシージャを作成する方がよいのはいつですか

私はアプリケーション全体で100%アドホックSQLを使用しています。私の仲間は、パフォーマンスとセキュリティを強化するために、ストアドプロシージャに変換することを勧めました。これは私の心に疑問を投げかけました、スピードとセキュリティに加えて、アドホックSQLクエリに固執する他の理由はありますか?

37
Luke101

SQL Serverは、アドホッククエリの実行プランをキャッシュするため、(最初の呼び出しにかかる時間を割り引いて)2つのアプローチは速度の点で同じになります。

一般に、ストアドプロシージャの使用とは、アプリケーションに必要なコードの一部(T-SQLクエリ)を取得し、ソース管理されていない場所に配置することを意味します(canありますが、通常はis n't)であり、知らないうちに他の人が変更できる場所です。

mayのような中央の場所にクエリを配置することは、それらが表すデータにアクセスする必要のあるさまざまなアプリケーションの数に応じて、良いことです。私は一般的に、アプリケーションが使用するクエリをアプリケーションコード自体に常駐させる方がはるかに簡単だと感じています。

1990年代半ば、従来の通念では、SQL Serverのストアドプロシージャはパフォーマンスが重要な状況に移行する方法であり、当時は間違いなくそうでした。ただし、このCWの背後にある理由は、長い間有効ではありませんでした。

更新:また、ストアドプロシージャの実行可能性をめぐる議論では、プロシージャを防御するためにSQLインジェクションを防止する必要が頻繁に発生します。確かに、文字列の連結を介してアドホッククエリを組み立てることが正しいことだと考える人は誰もいません(ただし、これはuserを連結している場合にのみSQLインジェクション攻撃にさらされます)入力)。明らかに、アドホッククエリはパラメータ化する必要があります。これは、SQLインジェクション攻撃のベッドの下の怪物を防ぐだけでなく、プログラマーとしての生活を一般的に楽にするためです(シングルをいつ使用するかを理解する必要がある場合を除きます)。あなたの値の周りの引用符)。

更新2:私はさらに調査を行いました。 このMSDNホワイトペーパー に基づくと、答えは、クエリでの「アドホック」の意味に正確に依存しているようです。たとえば、次のような単純なクエリ:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

...will実行プランがキャッシュされます。さらに、クエリには特定の不適格要素(1つのテーブルからの単純なSELECT以外のほとんどすべて)が含まれていないため、SQL Serverは実際にクエリを「自動パラメータ化」し、リテラル定数「5」をパラメータに置き換えてキャッシュします。パラメータ化されたバージョンの実行プラン。つまり、thisアドホッククエリを実行すると、次のようになります。

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23

...キャッシュされた実行プランを使用できるようになります。

残念ながら、自動パラメータ化の不適格なクエリ要素のリストは長いため(たとえば、DISTINCTTOPUNIONGROUP BYORなどの使用を忘れてください)、パフォーマンスを期待することはできません。

次のように、自動パラメータ化されない「超複雑な」クエリがある場合。

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23

...クエリの正確なテキストによって引き続きキャッシュされるため、アプリケーションが同じリテラルの「ハードコードされた」値を使用してこのクエリを繰り返し呼び出すと、最初のクエリの後の各クエリは、キャッシュされた実行プランを再利用します(およびしたがって、ストアドプロシージャと同じくらい高速です)。

リテラル値が変更された場合(たとえば、表示されたデータのフィルタリングや並べ替えなどのユーザーアクションに基づいて)、クエリはキャッシュの恩恵を受けません(最近のクエリと誤って完全に一致する場合を除く)。

「アドホック」クエリを使用したキャッシュのメリットを享受する方法は、それらをパラメータ化することです。次のようにC#でその場でクエリを作成します。

int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " + 
        itemCount.ToString();

間違っている。正しい方法(ADO.Netを使用)は次のようになります。

using (SqlConnection conn = new SqlConnection(connStr))
{
    SqlCommand com = new SqlCommand(conn);
    com.CommandType = CommandType.Text;
    com.CommandText = 
        "DELETE FROM tblSTUFF WHERE ITEM_COUNT > @ITEM_COUNT";
    int itemCount = 5;
    com.Parameters.AddWithValue("@ITEM_COUNT", itemCount);
    com.Prepare();
    com.ExecuteNonQuery();
}

クエリにはリテラルが含まれておらず、すでに完全にパラメータ化されているため、同じパラメータ化されたステートメントを使用する後続のクエリは、キャッシュされたプランを使用します(異なるパラメータ値で呼び出された場合でも)。ここでのコードは、とにかくストアドプロシージャを呼び出すために使用するコードと実質的に同じであることに注意してください(唯一の違いはCommandTypeとCommandTextです)。したがって、クエリのテキストを「ライブ」にする場所に多少なります。 "(アプリケーションコードまたはストアドプロシージャ内)。

最後に、「アドホック」クエリとは、さまざまな列、テーブル、フィルタリングパラメータなどを使用して、クエリを動的に構築していることを意味します。

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`
    ORDER BY LASTNAME DESC

...それならあなたはほとんどca n'tこれをストアドプロシージャで行う(礼儀正しい社会では話されないEXECハックなしで)ので、要点は議論の余地あり。

更新3:これが唯一の本当に良いperformance-related理由(私が考えることができる)ですとにかく)ストアドプロシージャを使用するため。クエリが実行プランのコンパイルプロセスに実際の実行よりも大幅に時間がかかり、クエリの呼び出し頻度が低い場合(たとえば、月次レポートなど)、クエリをストアドプロシージャに配置すると、 SQL Serverに、コンパイルされたプランをキャッシュに保持して、来月頃になるようにします。しかし、それが本当かどうかは私を打ち負かします。

71
MusiGenesis

ストアドプロシージャについては、魔法のように高速または安全にするものは何もありません。適切に設計されたストアドプロシージャは、特定の種類のタスクで高速になる場合がありますが、その逆はアドホックSQLにも当てはまります。

最も生産性の高い方法をコーディングします。

「速くする直前に作ってください。」 -ブライアン・カーニハン

19
Bill Karwin

ストアドプロシージャを作成していない場合は、 パラメータ化されたクエリ を調べてください。パラメータの連結を含めてSQLを自分で構築する場合は、 SQLインジェクション攻撃 を招きます。

12
Eric J.

このトピックに関連して、自分自身を非難すべきいくつかの神話があります。

神話1:ストアドプロシージャはプリコンパイルされています
http://scarydba.wordpress.com/2009/09/30/pre-compiled-stored-procedures-fact-or-myth/

神話2:アドホックSQLクエリは実行プランを再利用しません:http://scarydba.wordpress.com/2009/10/05/ad-hoc-queries-dont-reuse-execution-plans-myth-or-fact /

データベースをどうしてもロックダウンする必要がある場合、IMHOプロシージャにはEdgeがあります。このような状況では、ストアドプロシージャを実行する権限しか持たないアカウントを使用できます。さらに、DBAの観点から、アプリとデータベースの間に抽象化レイヤーを提供できます。

同様に、動的SQLは、クエリの一部を変更する必要があり、動的である必要がある場合に適しています。または、複数のデータベースに移植する必要があることがわかっている場合。

ユーザーが入力したすべての値がパラメーター化されている限り、どちらもSQLインジェクションに関して同じように安全です。

9
Daniel Auger

このスレッドのパフォーマンス、キャッシング、セキュリティについてはすでに多くの意見が述べられていますが、これらの点については繰り返しません。このスレッドでまだ読んでいないことがいくつかあります。それは、移植性の問題とラウンドトリップです。

  • プログラミング言語間でのアプリケーションの最大の移植性に関心がある場合は、ストアドプロシージャを使用することをお勧めします。アプリ外のデータベースに格納するプログラムロジックが多いほど、別のフレームワークに移動する場合に再コーディングする必要が少なくなります。言語。さらに、ストアドプロシージャを呼び出すコードは、実際の生のSQL自体よりもはるかに小さいため、アプリケーションコードのデータベースインターフェイスのフットプリントは小さくなります。
  • 複数のアプリケーションで同じロジックが必要な場合、ストアドプロシージャは、他のアプリケーションで再利用できるロジックの単一の定義を持つと便利です。ただし、アプリケーション間で共有するライブラリでそのロジックを分離することもできるため、この利点はしばしば誇張されます。もちろん、アプリケーションが異なる言語である場合、別の言語で記述されたライブラリにリンクするよりも、言語のdbインターフェイスを介してプロシージャを呼び出す方がおそらく簡単であるため、ストアドプロシージャには真のメリットがあります。
  • RDBMSの移植性に関心がある場合は、ストアドプロシージャが最大の問題の1つになる可能性があります。すべてのメジャーおよびマイナーRDBMS-esのコア機能は非常に似ています。最大の違いは、ストアドプロシージャの構文と使用可能な組み込み機能にあります。

往復について:

  • 複数のSQLステートメントを必要とする多数のマルチステートメントトランザクション、または一般にアプリケーション内の関数がある場合、それらの複数のステートメントをストアドプロシージャ内に配置すると、パフォーマンスが向上する可能性があります。その理由は、ストアード・プロシージャーを呼び出す(そして、場合によってはそれから複数の結果を返す)ことは、たった1回のラウンドトリップであるためです。生のSQLでは、SQLステートメントごとに(少なくとも)1回のラウンドトリップがあります。
5
Roland Bouman

アドホッククエリがいつメリットをもたらすかわかりません。友人とこの同じ質問について話し合ったところ、ストアドプロシージャを支持する次のことがわかりました(明らかなキャッシュ/ SQLインジェクションの問題は別として)。

1)コードの可読性:アプリケーションにいくつかの毛深いSQLコードが埋め込まれていると、読みやすく、理解しにくくなります。多くのコードを使用する代わりに、ストアドプロシージャの命名規則を使用して、その機能を正確に説明する方がはるかに簡単です。リファクタリング時に使用しようとしているのと同じ原則ではありません。

2)再デプロイせずにアプリケーションを改善する機能:バグやパフォーマンスの低下のためにロジックを微調整する必要がある場合、アプリケーションにSQLコードを埋め込むことは、(データセットが変更されていない場合でも)再デプロイする必要があることを意味します。 。ストアドプロシージャにある場合、再デプロイする必要があるのはプロシージャだけです。また、DBAに変更を加えて、プロシージャを操作することでクエリの全体的なパフォーマンスを向上させる魔法を実行します。組み込みバージョンで作業している場合、これを行うのははるかに困難です。

3)ネットワークトラフィック:多くのSQLコードを渡すと、ネットワークを介して送信されるメッセージ(またはRPC呼び出し)のサイズが大きくなり、要求によるパフォーマンスの低下につながる可能性があります。特に、同じ通話を毎回行うユーザーが多い場合。

これがお役に立てば幸いです。

乾杯、ワーグナー。

2
Wagner Silveira

私はアプリケーション全体で100%アドホックSQLを使用しています。私の仲間は、パフォーマンスとセキュリティを強化するために、ストアドプロシージャに変換することを勧めました。

実際に問題が発生するまで、パフォーマンスについて心配する必要はありません。たとえば、誰かがあなたのアプリケーションを使用していて、それが遅いと不平を言っています。その時点に到達するまでは、アプリケーションの改善に時間を費やすのが最善です。

セキュリティでは、努力とリスクのバランスを取る必要があります。あなたのサイトが価値のあるものを何も保存していない場合、そこにある多数のWebサイトで証明されているように、SQLインジェクションでさえ完全に許容できるリスクです:)

2
Andomar

新しいSQLサーバーに切り替える必要があり、古いSQLサーバーにアクセスできない場合の移植性。

私は、元の開発者が解雇された後、サーバーへのアクセスを許可しないいくつかのプロジェクトに取り組んできました。そのため、私たちが持っていなかったストアドプロシージャにロックされていたため、多くのクエリを自分で書き直す必要がありました。表示する許可。すべてのクエリがソースコードに含まれていれば、はるかに簡単になります。

2
Robert

正直なところ、SQLインジェクションはクエリをパラメータ化することで防ぐことができ(たとえばODBCParametersを調べてください)、これらのパラメータをSQLインジェクションできないようにクエリを構築できます。例えば...

DECLARE @param varchar(50)
SELECT @param = ?
SELECT * FROM TABLE WHERE NAME = @param

ODBCパラメータを使用して内部クエリを実行する安全な方法です。ただし、ストアドプロシージャを使用することにはいくつかの利点があります。

  1. 一部のODBCドライバーは複数のクエリ要求を処理する方法を知らないため、アドホックな方法で使用すると、複数の関数またはカーソルを持つ複雑なSQLが台無しになる可能性があります
  2. これは、ビジネスロジックをデータベース呼び出しから分離します。これにより、(アプリケーション開発者は)データベースの構造について何も知らなくてもアプリケーションを開発でき、専任のDBAまたはSQL開発者はSQLを微調整できます。
  3. ストアドプロシージャは事前にコンパイルされているため、何度も繰り返すと高速になりますが、非常に頻繁に呼び出される場合に限ります(つまり、監視プログラム)。

すべてが言われ、行われるとき、それは設計上の決定です。移植性を考慮しないでください。SQLdevに、ストアプロシージャを適用し、プログラムのインストール時にそれらを実行するためのスクリプトを提供させることができます:)

お役に立てれば

1
TerrorAustralis

ストアドプロシージャの利点の1つは、中間計算のためにデータをネットワーク経由で転送する必要がないことです。多くの計算で単一の値が得られる場合、ストアドプロシージャはより高速になります。

1
SeaDrive

私の経験から言えば、それぞれの方法に有利な点を置きたいと思います。

アドホックSQLのポイント:

アドホックSQLが(最初は)それほど苦痛ではない場合があります。これには、広範囲の(そして動的な)検索パラメーターが含まれます。たとえば、アカウント検索と、3倍のパラメータを含むコンパニオンの高度なアカウント検索があるとします。一部のフィールドは簡単です(アカウント番号)。他の人は非常に苦痛かもしれません(アドレス行1の部分文字列検索!)そして最悪の場合、ほとんどのパラメータは必要ありません。

これにより、パフォーマンスチューニングは簡単ではありません。この場合、パラメーターの組み合わせが非常に多いため、実行プランのキャッシュ(私の経験はMS SQLでの経験です)はbackfireになります。たとえば、アカウント番号とセールスマン番号のみを提供するための実行プランは、住所検索とそれらが持つブランドを提供する別の検索セットにとっては非常に悪い可能性があります。アドホックSQLには、提供されたさまざまなパラメーターセットに基づいて再コンパイルするという副作用があり、実行プランのキャッシュの問題を回避できます。

もちろん、上記はストアドプロシージャでも実行できますが、実行プランのキャッシュの問題を回避するには、ストアドプロシージャ内でrecompileコマンドを起動する必要があります。動的SQL構造をストアドプロシージャ内に配置できると主張することもできますが、これはアドホックSQLを別の場所に移動するだけです。まだアドホックSQL。

ストアドプロシージャのポイント:

ストアドプロシージャはAPIと見なすことができます。エンタープライズ環境で作業したことがある場合は、まったく同じことを行うさまざまなアプリケーションが存在する可能性があります。たとえば、イベントテーブルは、経理、販売、監査、顧客関係管理などのさまざまなソフトウェアから挿入できます。これらのプログラムは、同じグループの人々(たとえば、さまざまな下位区分)によって維持されない場合がありますが、最終的にはアクセスできます。同じ基盤となるデータベースに。

この場合、アドホックSQLを使用することは、ソースコード管理の悪夢になります。これは、アドホックSQLの複数のバージョンが同じ機能を実行し、バージョンごとに異なる副作用が発生する可能性があるためです。私は現在この状況に対処していますが、それは面白くありません。この場合のストアドプロシージャは再利用できるため、データベースコードを一元管理できます。

1

私の答えは少し話題から外れているかもしれませんが、とにかく:

ビューを操作するときに役立つ場合があります。たとえば、(使用していない)Hibernateは、ビューのサポートがかなり悪いです。それを使用してビューをクエリする必要がある場合は、常に生のSQLを使用します。これが機能する唯一のものだからです。

1
Lars Andren

他に誰がデータベースを使用しているかによって異なります。 1つのアプリケーションのみがdbを使用する場合、パラメーター化されたクエリには、ソースに存在するという利点があります。

他のアプリケーションがdbを使用する場合、dbaは一般的なストアドプロシージャをdbに配置する必要があります。

0
wisty

「正しい」答えはありません。それぞれの状況によります。

誰かがこれに言及しましたか?ストアドプロシージャは通常、すべてのフィールドを返します。必要なフィールドのバリエーションごとに1つを作成することは不可能です。アドホックでは、必要なものだけを指定できます。ただし、任意の種類のエンティティ(カスタムオブジェクト、EFなど)を使用している場合は、とにかくすべてのフィールドを返す可能性があります。

0

おそらくパフォーマンス上の利点はありませんが、保守性のために、SQLに構文エラーが発生しないように、LINQ2SQLなどの使用を検討することをお勧めします。

0
Andrew Lewis

アドホッククエリは、アプリケーションロジックに柔軟性をもたらしますが、ほとんどの場合、パフォーマンスに代償を払います。

パフォーマンスに関心がある場合は、データベースがクエリを「事前に最適化」できるようにするため、またはキャッシュレイヤーを許可するために、ストアドプロシージャ、またはより静的な形式のクエリを調べる必要があることを友人に同意するでしょう(存在する場合)クエリ結果をキャッシュする可能性があります。

毎回その場でクエリを生成する場合、データベースはパフォーマンスに関してまったく役に立たない可能性があります。

0
Andy White
  1. ストアドプロシージャを作成する必要はありません
  2. 一般に、アドホックSQLは十分に高速です。パラメータ化されたクエリを使用して、速度を上げることができます。
  3. 文字列ベースのSQLを「コンパイルされた」SQLにコンパイルしてから実行すると、はるかに高速になります。
  4. 通常、SQLのパフォーマンスのボトルネックは、上記のいずれでもないクエリです。
0
Clarus