動的SQLとストアドプロシージャは、SQLServerの最も重要なコンポーネントの2つです。この記事では、それぞれの長所と短所、およびそれらをいつ使用するかについて説明します。
パフォーマンス
誰もがこの質問に対する答えを知っています。ストアドプロシージャは、パフォーマンスの点で動的SQLよりも優れています。ストアドプロシージャはサーバーメモリにキャッシュされ、その実行は動的SQLよりもはるかに高速です。残りのすべての変数が一定に保たれている場合、ストアドプロシージャは動的SQLよりも優れています。
関心の分離
関心の分離という点では、ストアドプロシージャは動的SQLの受け渡しに勝っています。
ストアドプロシージャを使用すると、データベースロジックをビジネスロジックから分離しておくことができます。したがって、ビジネスロジックでエラーが発生した場合は、アプリケーションコードを変更するだけで済みます。逆に、データベースロジックに問題がある場合は、ストアドプロシージャのみを変更する必要があります。さらに、ストアドプロシージャが更新された場合、アプリケーションコードを再コンパイルしてデプロイする必要はありません。
クライアントコードで動的SQLクエリを使用する場合、SQLクエリでエラーが発生した場合は、アプリケーションコードを更新する必要があります。これは、アプリケーションコードを再コンパイルしてデプロイする必要があることを意味します。
ネットワークトラフィック
ストアドプロシージャを実行するには、プロシージャ名とパラメータ(存在する場合)のみをネットワーク経由で送信する必要があるため、ストアドプロシージャは動的SQLよりも少ないネットワークトラフィックを生成します。
動的SQLを実行するには、完全なクエリをネットワーク経由で送信する必要があり、特にクエリが非常に大きい場合は、ネットワークトラフィックが増加します。
SQLインジェクション攻撃
ストアドプロシージャはSQLインジェクション攻撃に対して脆弱ではありません。
パラメータ化されたクエリが使用されていない場合、動的SQLクエリはSQLインジェクション攻撃に対して脆弱であり、テーブルまたは列の名前がパラメータとして渡された場合、パラメータ化されたクエリは動的SQLでは使用できません。
この場合の回避策は、コードネーム関数を使用してSQLインジェクション攻撃を防ぐことができることです。
キャッシュされたクエリプランの再利用性
ストアドプロシージャを使用すると、キャッシュされたクエリプランを再利用できるため、データベースのパフォーマンスが向上します。動的SQLの場合、パラメーター化されたクエリを使用して、キャッシュされたクエリプランの再利用性を高める必要があります。パラメータ化されたクエリプランがない場合、SQLサーバーはパラメータを自動的に検出し、キャッシュされたクエリプランを生成して、パフォーマンスを向上させます。
ここで、キャッシュされたクエリプランの再利用性の恩恵を受けるのはOLTPシステムだけであることに言及するのが適切です。 OLAPシステムの場合、オプティマイザーの選択が変更され、OLAPシステムは独自のプランの恩恵を受けます。
メンテナンス
静的SQLを使用したストアドプロシージャは、保守が容易です。たとえば、ストアドプロシージャの静的SQLの場合、実行前に構文エラーをキャッチできます。ストアドプロシージャ内の動的SQLの場合、クエリの実行前に構文エラーをキャッチすることはできません。
さらに、ストアドプロシージャは関数に似ており、一度定義すると、スクリプト内のどこからでも呼び出すことができます。したがって、ストアドプロシージャを更新する場合は、1か所で更新するだけで済みます。ストアドプロシージャを呼び出すすべてのアプリケーションパーツは、更新されたバージョンにアクセスできます。ただし、欠点は、更新されたストアドプロシージャが必要ない場合に、これらのアプリケーションパーツも影響を受ける可能性があることです。動的SQLの場合、複数の場所でSQLスクリプトを作成する必要があるかもしれませんが、そのような場合、ある場所でスクリプトを更新しても、他の場所には影響しません。ストアドプロシージャを使用するか動的SQLを使用するかの決定は、アプリケーションの機能によって異なります。
セキュリティ
複数のアプリケーションがデータベースにアクセスする場合は、動的SQLよりもストアドプロシージャを使用する方が安全です。
ストアドプロシージャはセキュリティの追加レイヤーを提供しますが、動的SQLスクリプトのアクセス許可を制御する唯一の方法はユーザーコンテキストです。全体として、動的SQLの保護は、ストアドプロシージャと比較して面倒です。
依存関係の特定
リレーショナルデータベースでは、テーブルはデータベース内の他のテーブルに依存しています。
テーブルを削除したいが、その前に、すべてのテーブルの依存関係を調べたいというシナリオを考えてみましょう。または、簡単に言うと、削除するテーブルにアクセスするクエリを検索する必要があります。このような場合は、sp_dependsストアドプロシージャを使用できます。
ただし、sp_dependsは、ストアドプロシージャ内で静的SQLが使用されている依存関係のみを検出できます。動的SQLがテーブルに依存している場合、その依存関係はsp_dependsストアドプロシージャでは検出できません。簡単な例を使って、これが実際に動作しているところを見てみましょう。
ダミーデータの準備
静的および動的SQLの依存関係の概念を説明するのに役立つダミーデータを作成しましょう。
CREATE DATABASE deptest; USE deptest CREATE TABLE student ( Id int identity primary key, Name VARCHAR(50) NOT NULL, Gender VARCHAR(50) NOT NULL, Age int ) INSERT INTO student VALUES ('James', 'Male', 20), ('Helene', 'Female', 20), ('Sofia', 'Female', 20), ('Ed', 'Male', 20), ('Ron', 'Female', 20)
これで、テーブルといくつかのテストデータを含むテストデータベースができました。次に、学生テーブルにアクセスする2つのストアドプロシージャを作成しましょう。
最初のストアドプロシージャは静的SQLを使用して、学生テーブルからすべてのレコードを取得します。
USE deptest GO CREATE PROC spStatProc AS BEGIN SELECT * FROM student END
上記のスクリプトを実行します。このスクリプトは、deptestデータベース内にストアドプロシージャ「spStatProc」を作成します。
学生テーブルからすべてのレコードを取得する動的SQLを含む別のストアドプロシージャを作成しましょう。
USE deptest GO CREATE PROC spDynProc AS BEGIN DECLARE @query NVARCHAR(100) SET @query = 'SELECT * FROM student' EXECUTE sp_execute @query END
このスクリプトは、deptestデータベース内にストアドプロシージャ「spDynProc」を作成します。このストアドプロシージャは、動的SQLステートメントを使用して、学生テーブルからすべてのレコードを取得します。
これで、studentテーブルに依存する2つのストアドプロシージャができました。 1つには静的SQLが含まれ、もう1つには動的SQLが含まれます。
ただし、sp_dependsストアドプロシージャを実行し、それにstudentテーブルをパラメータとして渡すと、「spStatProc」ストアドプロシージャのみが取得されることがわかります。これは、静的SQLが含まれているためです。 「spDynProc」ストアドプロシージャには動的SQLが含まれているため、無視されます。
次のスクリプトを実行します。
USE deptest GO EXECUTE sp_depends student
次の出力が得られます:
[テーブルID=40 /]
sp_dependsが「spDynProc」依存関係を報告できず、「spStatProc」のみを報告したことがわかります。
複雑さ
多数のフィルターを使用していて、フィルター間に複数のAND句とOR句がある場合、ストアード・プロシージャーは非常に複雑になる可能性があります。一方、動的SQLを使用すると、フィルターのタイプに応じてWHERE句を動的に生成できます。これにより、非常に複雑なロジックを実装する場合は、動的SQLがより適切な選択肢になります。
結論
全体として、ストアドプロシージャは、ほとんどすべての面で動的SQLよりも優れています。これらは、より高速で安全で、保守が容易であり、必要なネットワークトラフィックも少なくて済みます。経験則として、ストアドプロシージャは、クエリを変更する必要がなく、クエリがそれほど複雑でないシナリオで使用する必要があります。ただし、クエリ内のテーブル名、列名、またはパラメータの数を頻繁に変更する場合は、実装戦略が単純なため、動的SQLの方が適しています。
便利なリンク
- 動的SQLとストアドプロシージャ
- 動的SQLを恐れないでください
- 高性能ストアドプロシージャの構築
- ストアドプロシージャのクラス