ストアドプロシージャは、実行パスを通じて(アプリケーションのインラインSQLよりも)より効率的であることを知っています。しかし、押されたとき、私はその理由についてあまり知らない。
この技術的な理由を知りたい(後で誰かに説明できるように)。
誰も私が良い答えを策定するのを手伝ってくれますか?
ストアドプロシージャは、実行パスを通じて(アプリケーションのインラインSQLよりも)より効率的であることを知っています。しかし、押されたとき、私はその理由についてあまり知らない。
この技術的な理由を知りたい(後で誰かに説明できるように)。
誰も私が良い答えを策定するのを手伝ってくれますか?
回答:
この感情はある時点では真実だったと思いますが、SQL Serverの現在のバージョンではそうではありません。全体的な問題は、SQL Serverはバッチレベルでしか最適化/コンパイルできないため、昔はアドホックSQLステートメントを適切に最適化できなかったことです。これで、ステートメントレベルの最適化が可能になりました。そのため、アプリケーションから適切にパラメーター化されたクエリは、ストアドプロシージャに埋め込まれたクエリと同じ実行プランを利用できます。
次の理由から、DBA側からのストアドプロシージャの方が好きです(そして、それらのいくつかはパフォーマンスに大きな影響を与える可能性があります)。
sys.sql_modules
特定のオブジェクトへの参照用の)を表示できると、全員の生活がずっと楽になります。SET ANSI_WARNINGS ON
、もう一方がを持ちSET ANSI_WARNINGS OFF
、それぞれが独自の計画のコピーを持つことになります。取得するプランは、使用するパラメーター、適切な統計などに依存します。それぞれの場合にクエリが最初に呼び出されると、異なるプランになり、パフォーマンスが大きく異なる可能性があります。とはいえ、この質問は技術的な議論よりも宗教的な議論を巻き起こす可能性があります。そのような事態が発生した場合、おそらくシャットダウンします。
私は提出者を尊重しますが、「宗教的な理由」ではなく、提供された回答に謙虚に同意しません。言い換えれば、ストアドプロシージャを使用するためのガイダンスの必要性を減らすマイクロソフトが提供している機能はないと考えています。
生のテキストSQLクエリの使用を支持する開発者に提供されるガイダンスには、多くの注意事項を記入する必要があります。そのため、最も慎重なアドバイスは、ストアドプロシージャの使用を大幅に奨励し、開発者チームがプラクティスに参加しないようにすることですSQL SPROC(ストアドプロシージャ)の外部で、コードにSQLステートメントを埋め込む、または生の、プレーンテキストのSQLベースのSQL要求を送信する。
なぜSPROCを使用するのかという質問に対する簡単な答えは、提出者が推測したとおりであると思います。SPROCは解析、最適化、コンパイルされます。そのため、クエリの静的な表現を保存したため、クエリ/実行プランはキャッシュされ、通常はパラメータによってのみ変更されますが、モーフィングの可能性があるコピー/貼り付けられたSQL文の場合はそうではありませんページ間およびコンポーネント/層から、また、多くの場合、データベース名を含めてさまざまなテーブルを呼び出し間で指定できる程度に変更されます。このタイプのダイナミックを許可するアドホックSQL送信により、非常に厳しいルールに従って、DBエンジンがアドホックステートメントのクエリプランを再利用する可能性が大幅に低下します。ここでは、動的アドホッククエリ(提起された質問の精神)と効率的なSystem SPROC sp_executesqlの使用を区別しています。
具体的には、次のコンポーネントがあります。
「アドホックステートメント」と呼ばれるWebページからSQLステートメントが発行されると、エンジンは既存の実行プランを探してリクエストを処理します。これはユーザーから送信されたテキストであるため、有効な場合は取り込み、解析、コンパイル、および実行されます。この時点で、クエリコストはゼロになります。クエリコストは、DBエンジンがアルゴリズムを使用して、キャッシュから削除する実行プランを決定するときに使用されます。
アドホッククエリは、デフォルトでゼロの元のクエリコスト値を受け取ります。別のユーザープロセス(または同じプロセス)がまったく同じアドホッククエリテキストを続けて実行すると、現在のクエリコストは元のコンパイルコストにリセットされます。アドホッククエリのコンパイルコストはゼロであるため、これは再利用の可能性の前兆ではありません。明らかに、ゼロは最小値の整数ですが、なぜ排除されるのでしょうか?
メモリのプレッシャーが発生し、頻繁に使用されるサイトがある場合、DBエンジンはクリーンアップアルゴリズムを使用して、プロシージャキャッシュが使用しているメモリを再利用する方法を決定します。現在のクエリコストを使用して、排除する計画を決定します。ご想像のとおり、コストがゼロのプランはキャッシュから削除される最初のプランです。ゼロは本質的に「このプランの現在のユーザーまたは参照がない」ことを意味するためです。
したがって、このような計画は、メモリのプレッシャーが発生したときに最初に排除される可能性が非常に高くなります。
したがって、「ニーズを超えて」大量のメモリを備えたサーバーを構築している場合、ワークロードを処理するのに「十分な」メモリしかないビジーなサーバーほど頻繁にこの問題が発生することはありません。(申し訳ありませんが、アルゴリズムはそうではありませんが、サーバーのメモリ容量と使用率は多少主観的/相対的です。)
現在、1つまたは複数のポイントについて事実に誤りがある場合、私は間違いなく修正されることにオープンです。
最後に、著者は次のように書いています。
「現在、ステートメントレベルの最適化が行われているため、アプリケーションから適切にパラメーター化されたクエリは、ストアドプロシージャに埋め込まれたクエリと同じ実行プランを利用できます。」
著者は「アドホックワークロード用に最適化」オプションに言及していると思います。
その場合、このオプションを使用すると、完全なクエリプランをプロシージャキャッシュにすぐに送信することを避ける2段階のプロセスが可能になります。そこに小さなクエリスタブを送信するだけです。クエリスタブがまだプロシージャキャッシュにある間に、正確なクエリ呼び出しがサーバーに送り返されると、その時点で完全なクエリ実行プランがプロシージャキャッシュに保存されます。これにより、メモリが節約されます。これにより、メモリが圧迫された場合に、エビクションアルゴリズムがキャッシュされた大きなクエリプランよりも頻繁にスタブを追い出すことができます。繰り返しますが、これはサーバーのメモリと使用率に依存します。
ただし、デフォルトではオフになっているため、このオプションをオンにする必要があります。
最後に、多くの場合、開発者がページ、コンポーネント、その他の場所にSQLを埋め込む理由は、柔軟で、データベースエンジンに動的SQLクエリを送信したいからです。したがって、実際のユースケースでは、アドホッククエリをSQL Serverに送信するときに、同じテキスト(call-over-call)を送信することは、私たちが求めるキャッシュ/効率性と同様に発生することはほとんどありません。
追加情報については、以下を参照してください。
https://technet.microsoft.com/en-us/library/ms181055(v=sql.105).aspx
http://sqlmag.com/database-performance-tuning/don-t-fear-dynamic-sql
ベスト、
ヘンリー
TLDR:インラインSQLがパラメーター化されている限り、この2つの間に目立ったパフォーマンスの違いはありません。
これらが、ストアドプロシージャを徐々に段階的に廃止した理由です。
「ベータ」アプリケーション環境を実行します。これは、本番データベースを共有する本番と並行した環境です。dbコードはアプリケーションレベルにあり、db構造の変更はめったにないので、QAを超えて新しい機能を確認し、運用展開ウィンドウの外で展開を行うことができますが、運用機能と重要ではない修正を提供できます。アプリケーションコードの半分がDBにある場合、これは不可能です。
データベースレベル(タコ+ dacpacs)でdevopsを練習します。ただし、ビジネスレイヤー以上は基本的にパージして置換し、逆の回復を行うことができますが、データベースに適用する必要がある増分的で潜在的に破壊的な変更には当てはまりません。したがって、DBの展開をより軽く、頻度を少なくすることをお勧めします。
オプションのパラメータに同じコードのほぼ正確なコピーを避けるために、「where @var is null or @ var = table.field」パターンを使用することがよくあります。ストアドプロシージャを使用すると、意図はかなり異なりますが、同じ実行プランを取得する可能性が高いため、パフォーマンスの問題が発生するか、「再コンパイル」ヒントを使用してキャッシュされたプランを削除します。ただし、SQLの最後に「署名」コメントを追加する簡単なコードを使用すると、どの変数がnullであるかに基づいて異なるプランを強制できます(すべての変数の組み合わせに対して異なるプランとして解釈されるのではなく、nullとnullではない)。
SQLをその場で少し変更するだけで、結果に劇的な変更を加えることができます。たとえば、「Raw」と「ReportReady」という2つのCTEで終了するステートメントを作成できます。両方のCTEを使用する必要があることを示すものは何もありません
...
select * from {(format)} "
これにより、簡素化されたAPI呼び出しと、複雑なロジックを複製しないようにするために、より詳細に記述する必要があるレポートの両方に、まったく同じビジネスロジックメソッドを使用できます。
プロシージャを使用する正当な理由があります:
セキュリティ-アプリが通過しなければならない別のレイヤーがあります。アプリケーションサービスアカウントがテーブルにアクセスすることを許可されておらず、プロシージャに対する「実行」権限のみを持っている場合、追加の保護があります。コストがかかるため、これは当然のことではありませんが、可能性はあります。
再利用-DBに関連しないビジネスルールをバイパスしないようにするために、ビジネスレイヤーで再利用を主に行うべきだと思いますが、私たちはまだ職業上の低レベルの「どこでも使用」タイプのユーティリティプロシージャと機能を持っています。
procを実際にサポートしていないか、IMOを簡単に軽減できるいくつかの引数があります。
再利用-私はこれを「プラス」として言及しましたが、再利用は主にビジネス層で行われるべきであるとここでも述べたいと思いました。レコードを挿入するprocは、ビジネスレイヤーが他のdb以外のサービスもチェックしている可能性がある場合、「再利用可能」と見なすべきではありません。
キャッシュプランの膨張-これが問題になる唯一の方法は、パラメーター化するのではなく値を連結する場合です。プロシージャごとに複数のプランを取得することはめったにないという事実は、クエリに「または」がある場合に実際に痛いことがよくあります
ステートメントサイズ-プロシージャ名に対するSQLステートメントの余分なkbは、通常、戻ってくるデータに比べて無視できる程度です。エンティティに問題がなければ、私にも問題はありません。
正確なクエリを確認する-コード内でクエリを見つけやすくするには、呼び出し場所をコメントとしてコードに追加するだけです。コードをC#コードからssmsにコピー可能にすることは、クリエイティブな補間やコメントの使用と同じくらい簡単です。
//Usage /*{SSMSOnly_}*/Pure Sql To run in SSMS/*{_SSMSOnly}*/
const string SSMSOnly_ = "*//*<SSMSOnly>/*";
const string _SSMSOnly = "*/</SSMSOnly>";
//Usage /*{NetOnly_}{InterpolationVariable}{_NetOnly}*/
const string NetOnly_ = "*/";
const string _NetOnly = "/*";
SQLインジェクション-クエリをパラメーター化します。できた procが代わりに動的SQLを使用している場合、これは実際に元に戻すことができます。
デプロイメントのバイパス-データベースレベルでもdevopsを実践しているため、これはオプションではありません。
「アプリケーションが遅く、SSMSが速い」-これは、両側に影響するプランキャッシュの問題です。設定オプションは、THE ONE SET OFF変数の問題を修正するように見える新しいプランをコンパイルするだけです。これは、異なる結果が表示される理由にのみ答えます。設定オプション自体は、パラメータスニッフィングの問題を修正しません。
インラインSQL実行プランはキャッシュされません-単にfalseです。proc名と同じようにパラメーター化されたステートメントがすばやくハッシュされ、そのハッシュによってプランが検索されます。100%同じです。
明確にするために、ORMから生成されたコードではなく、生のインラインSQLについて話しています-せいぜいマイクロORMであるDapperのみを使用しています。