結果としてテーブルを返す必要があるT-SQLコードを実装する必要があるとしましょう。テーブル値関数または行のセットを返すストアドプロシージャを実装できます。何を使うべきですか?
要するに、私が知りたいのは:
関数とストアドプロシージャの主な違いは何ですか?どちらか一方を使用する場合、どのような考慮事項を考慮する必要がありますか?
回答:
このコードの結果を他のテーブルと組み合わせたい場合は、明らかにテーブル値関数を使用して、単一のSELECTステートメントで結果を作成できます。
通常、階層があります(ビュー<TV Function <Stored Proc)。それぞれでより多くのことを実行できますが、出力を構成する能力、およびオプティマイザが実際に関与する能力は、機能が増加するにつれて減少します。
したがって、希望する結果を最小限に表現できるものを最低限使用してください。
関数は確定的でなければならず、データベースに変更を加えるために使用することはできませんが、ストアドプロシージャでは挿入や更新などを行うことができます。
関数は大きく複雑なクエリに対して大きなスケーラビリティの問題を引き起こすため、関数の使用を制限する必要があります。これらはクエリオプティマイザーにとっては「ブラックボックス」のようなものになり、関数を使用する場合とクエリにコードを挿入する場合でパフォーマンスに大きな違いが見られます。
しかし、これらは非常に特殊なケースでのテーブル値の戻りに間違いなく役立ちます。
カンマ区切りのリストを解析する必要がある場合、プロシージャへの配列の受け渡しをシミュレートするために、関数はリストをテーブルに変換できます。ストアドプロシージャにテーブルを渡すことはまだできないため(2008では可能です)、これはSQL Server 2005の一般的な方法です。
ストアドプロシージャが次の基準を満たしている場合、テーブル値関数として書き換えるのに適しています。
ロジックは単一のSELECTステートメントで表現できますが、パラメーターが必要なため、ビューではなくストアドプロシージャです。
ストアドプロシージャは、テーブル変数を除いて、更新操作を実行しません。
動的EXECUTEステートメントは必要ありません。
ストアドプロシージャは1つの結果セットを返します。
ストアドプロシージャの主な目的は、一時テーブルに読み込まれる中間結果を構築することです。一時テーブルは、SELECTステートメントで照会されます。
ストアドプロシージャと関数の興味深い違いをいくつか書きます。
関数では非決定的関数を使用できませんが、ストアドプロシージャでは非決定的関数を使用できます。ここで質問が出てきます、非決定的関数とは何ですか。Ansは:-
非決定的関数とは、getdate()のように、同じ入力値に対して異なる時間に異なる出力を返す関数です。実行すると常に異なる値を返します。
例外:-
SQL 2000より前の以前のバージョンのSQLサーバーでは、ユーザー定義関数でgetdate()関数を使用できませんが、バージョン2005以降では、ユーザー定義関数内でgetdate()関数を使用できます。
Newid()は非決定的関数のもう1つの例ですが、ユーザー定義関数では使用できませんが、ストアドプロシージャで使用できます。
ストアドプロシージャ内でDML(挿入、更新、削除)ステートメントを使用できますが、物理テーブルまたは永続テーブルの関数でDMLステートメントを使用することはできません。関数でDML操作を実行する場合は、永続テーブルではなくテーブル変数に対して実行できます。
関数内でエラー処理を使用することはできませんが、ストアドプロシージャでエラー処理を実行することはできます。
プロシージャはゼロまたはnの値を返すことができますが、関数は必須の1つの値を返すことができます。
プロシージャは入出力パラメータを持つことができますが、関数は入力パラメータのみを持つことができます。
プロシージャではselectとDMLステートメントを使用できますが、関数ではselectステートメントのみを使用できます。
関数はプロシージャから呼び出すことができますが、プロシージャは関数から呼び出すことはできません。
例外はプロシージャのtry-catchブロックで処理できますが、try-catchブロックは関数では使用できません。
トランザクション管理に進むことができますが、機能することはできません。
プロシージャはselect文で使用できませんが、関数はselect文に埋め込むことができます。
UDF(ユーザー定義関数)は、SQLステートメントでWHERE
/ HAVING
/ SELECT
セクションの任意の場所で使用できますが、ストアドプロシージャは使用できません。
テーブルを返すUDFは、別の行セットとして扱うことができます。これはJOIN
、他のテーブルと一緒に使用できます。
インラインUDFは、パラメーターを取りJOIN
、やその他の行セット操作で使用できるビューと考えることができます。
テーブル値関数とストアドプロシージャの両方で同じコード(長いSELECTステートメント)を実行し、実行時間の長いロジックを使用していくつかのテストを実行しました。
私の意見では、結果セットを返すためにストアドプロシージャではなく常にテーブル値関数を使用します。これにより、後で結合するクエリでロジックがはるかに簡単で読みやすくなり、同じロジックを再利用できるようになるためです。パフォーマンスへの影響を回避するために、「オプション」パラメーター(つまり、パラメーターにNULLを渡すことができます)を使用して、関数が結果セットをより速く返すようにします。例:
CREATE FUNCTION dbo.getSitePermissions(@RegionID int, @optPersonID int, optSiteID int)
AS
RETURN
SELECT DISTINCT SiteID, PersonID
FROM dbo.SiteViewPermissions
WHERE (@optPersonID IS NULL OR @optPersonID = PersonID)
AND (@optSiteID IS NULL OR @optSiteID = SiteID)
AND @RegionID = RegionID
これにより、さまざまな状況でこの関数を使用でき、パフォーマンスに大きな影響を与えません。これは後でフィルタリングするよりも効率的だと思います:
SELECT * FROM dbo.getSitePermissions(@RegionID) WHERE SiteID = 1
私はいくつかの関数でこの手法を使用しており、このタイプの「オプション」パラメーターの長いリストを使用する場合もあります。
上記のように、関数はより読みやすく/構成可能/自己文書化されますが、一般的にはパフォーマンスが低く、次のような結合でそれらに慣れると、パフォーマンスが大幅に低下する可能性があります
SELECT *
FROM dbo.tvfVeryLargeResultset1(@myVar1) tvf1
INNER JOIN dbo.tvfVeryLargeResultset1(@myVar2) tvf2
ON (tvf1.JoinId = tvf2.JoinId)
多くの場合、(許容できないパフォーマンスコストで)tvfが排除できるコードの冗長性を受け入れる必要があります。
まだ言及していないもう1つの点は、マルチステートメントtvf内でデータベースの状態変更一時テーブルを使用できないことです。 一時テーブルと最も機能的に同等なメカニズムは、メモリテーブル変数内の非状態変更です。大規模なデータセットの場合、一時テーブルはテーブル変数よりもパフォーマンスが高い可能性があります。(他の代替手段には、動的テーブルと共通テーブル値式が含まれますが、ある程度の複雑さで、これらは適切なオプションIMOでなくなります。)
ストアドプロシージャは事前にコンパイルされたクエリであり、高速に実行され、SQLインジェクションから節約されます。0またはNの値を返すことができます。ストアドプロシージャ内でDML操作を実行できます。プロシージャ内で関数を使用したり、selectクエリで関数を使用したりできます。関数は、任意の値を返すために使用され、DML操作は関数では実行できません。関数には、スカラーとテーブル値の2つのタイプがあります。スカラー関数は単一の値を返し、テーブル値の関数はテーブルの行を返すために使用されます。