関数とストアドプロシージャ


88

結果としてテーブルを返す必要があるT-SQLコードを実装する必要があるとしましょう。テーブル値関数または行のセットを返すストアドプロシージャを実装できます。何を使うべきですか?

要するに、私が知りたいのは:

関数とストアドプロシージャの主な違いは何ですか?どちらか一方を使用する場合、どのような考慮事項を考慮する必要がありますか?


1
これは完璧な答えのようです:stackoverflow.com/a/1179778/365188
Ozair Kafray、2015年

回答:


51

このコードの結果を他のテーブルと組み合わせたい場合は、明らかにテーブル値関数を使用して、単一のSELECTステートメントで結果を作成できます。

通常、階層があります(ビュー<TV Function <Stored Proc)。それぞれでより多くのことを実行できますが、出力を構成する能力、およびオプティマイザが実際に関与する能力は、機能が増加するにつれて減少します。

したがって、希望する結果を最小限に表現できるものを最低限使用してください。


50

関数は確定的でなければならず、データベースに変更を加えるために使用することはできませんが、ストアドプロシージャでは挿入や更新などを行うことができます。

関数は大きく複雑なクエリに対して大きなスケーラビリティの問題を引き起こすため、関数の使用を制限する必要があります。これらはクエリオプティマイザーにとっては「ブラックボックス」のようなものになり、関数を使用する場合とクエリにコードを挿入する場合でパフォーマンスに大きな違いが見られます。

しかし、これらは非常に特殊なケースでのテーブル値の戻りに間違いなく役立ちます。

カンマ区切りのリストを解析する必要がある場合、プロシージャへの配列の受け渡しをシミュレートするために、関数はリストをテーブルに変換できます。ストアドプロシージャにテーブルを渡すことはまだできないため(2008では可能です)、これはSQL Server 2005の一般的な方法です。


1
しかし、あなたは、ストアドプロシージャにXMLを送信することができます:stackoverflow.com/questions/144550/...
cllpse

2
誤って、MS-SQLサーバーのgetdateなど、ほとんどのSQLサーバー関数は非決定的です。ODBC関数のみが正規関数です(=はるかに高速+インデックス可能)...しかし、あなたは非常に正しいです。パフォーマンス上の理由から、クエリでの関数の使用をできるだけ制限する必要があります。
Stefan Steiger

45

ドキュメントから

ストアドプロシージャが次の基準を満たしている場合、テーブル値関数として書き換えるのに適しています。

  • ロジックは単一のSELECTステートメントで表現できますが、パラメーターが必要なため、ビューではなくストアドプロシージャです。

  • ストアドプロシージャは、テーブル変数を除いて、更新操作を実行しません。

  • 動的EXECUTEステートメントは必要ありません。

  • ストアドプロシージャは1つの結果セットを返します。

  • ストアドプロシージャの主な目的は、一時テーブルに読み込まれる中間結果を構築することです。一時テーブルは、SELECTステートメントで照会されます。


12

ストアドプロシージャと関数の興味深い違いをいくつか書きます。

  • selectクエリでは関数を使用できますが、selectクエリではストアドプロシージャを使用できません。
  • 関数では非決定的関数を使用できませんが、ストアドプロシージャでは非決定的関数を使用できます。ここで質問が出てきます、非決定的関数とは何ですか。Ansは:-

    非決定的関数とは、getdate()のように、同じ入力値に対して異なる時間に異なる出力を返す関数です。実行すると常に異なる値を返します。

    例外:-

    SQL 2000より前の以前のバージョンのSQLサーバーでは、ユーザー定義関数でgetdate()関数を使用できませんが、バージョン2005以降では、ユーザー定義関数内でgetdate()関数を使用できます。

    Newid()は非決定的関数のもう1つの例ですが、ユーザー定義関数では使用できませんが、ストアドプロシージャで使用できます。

  • ストアドプロシージャ内でDML(挿入、更新、削除)ステートメントを使用できますが、物理テーブルまたは永続テーブルの関数でDMLステートメントを使用することはできません。関数でDML操作を実行する場合は、永続テーブルではなくテーブル変数に対して実行できます。

  • 関数内でエラー処理を使用することはできませんが、ストアドプロシージャでエラー処理を実行することはできます。


MySQL関数でDML操作がサポートされるのはなぜですか?
ジョーイピント

@JoeyPinto。myNONsqlはSQLの問題ではないからです。もちろん、追加機能はありますが、基本はありません。
PerformanceDBA

8
  1. プロシージャはゼロまたはnの値を返すことができますが、関数は必須の1つの値を返すことができます。

  2. プロシージャは入出力パラメータを持つことができますが、関数は入力パラメータのみを持つことができます。

  3. プロシージャではselectとDMLステートメントを使用できますが、関数ではselectステートメントのみを使用できます。

  4. 関数はプロシージャから呼び出すことができますが、プロシージャは関数から呼び出すことはできません。

  5. 例外はプロシージャのtry-catchブロックで処理できますが、try-catchブロックは関数では使用できません。

  6. トランザクション管理に進むことができますが、機能することはできません。

  7. プロシージャはselect文で使用できませんが、関数はselect文に埋め込むことができます。

  8. UDF(ユーザー定義関数)は、SQLステートメントでWHERE/ HAVING/ SELECTセクションの任意の場所で使用できますが、ストアドプロシージャは使用できません。

  9. テーブルを返すUDFは、別の行セットとして扱うことができます。これはJOIN、他のテーブルと一緒に使用できます。

  10. インラインUDFは、パラメーターを取りJOIN、やその他の行セット操作で使用できるビューと考えることができます。


6

関数がある場合は、SQLステートメントの一部として使用できます。たとえば、

SELECT function_name(field1) FROM table

ストアドプロシージャの場合、この方法では機能しません。


1
彼はテーブルの値を返す関数について話していたと思います。
wcm 2008年

1
さて、私は一般的に話している。しかし、私の特定のケースでは、現在、ストアドプロシージャまたはテーブル値関数の間にいます。
Auron、

5

テーブル値関数とストアドプロシージャの両方で同じコード(長い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

私はいくつかの関数でこの手法を使用しており、このタイプの「オプション」パラメーターの長いリストを使用する場合もあります。


4

私が返すのは、影響のない単一のテーブルだけの場合、個人的にテーブル値関数を使用します。基本的に私はそれらをパラメーター化されたビューのように扱います。

複数のレコードセットを返す必要がある場合、またはテーブルで値が更新される場合は、ストアドプロシージャを使用します。

私の2セント


4

上記のように、関数はより読みやすく/構成可能/自己文書化されますが、一般的にはパフォーマンスが低く、次のような結合でそれらに慣れると、パフォーマンスが大幅に低下する可能性があります

SELECT *
FROM dbo.tvfVeryLargeResultset1(@myVar1) tvf1
INNER JOIN dbo.tvfVeryLargeResultset1(@myVar2) tvf2
    ON (tvf1.JoinId = tvf2.JoinId)

多くの場合、(許容できないパフォーマンスコストで)tvfが排除できるコードの冗長性を受け入れる必要があります。

まだ言及していないもう1つの点は、マルチステートメントtvf内でデータベースの状態変更一時テーブルを使用できないことです。 一時テーブルと最も機能的に同等なメカニズムは、メモリテーブル変数内の非状態変更です。大規模なデータセットの場合、一時テーブルはテーブル変数よりもパフォーマンスが高い可能性があります。(他の代替手段には、動的テーブルと共通テーブル値式が含まれますが、ある程度の複雑さで、これらは適切なオプションIMOでなくなります。)


1

私は両方のパフォーマンステストを行います。おそらく、spアプローチまたは派生テーブルは関数よりも大幅に高速であり、そうであればそのアプローチを使用する必要があります。一般的に、パフォーマンスを独占する可能性があるため、関数は使用しません。


1

それは依存します:)別のプロシージャでテーブル値の結果を使用したい場合は、TableValued関数を使用した方がよいでしょう。結果がクライアントのものである場合、通常はストアドプロシージャの方が適しています。


-1

ストアドプロシージャは事前にコンパイルされたクエリであり、高速に実行され、SQLインジェクションから節約されます。0またはNの値を返すことができます。ストアドプロシージャ内でDML操作を実行できます。プロシージャ内で関数を使用したり、selectクエリで関数を使用したりできます。関数は、任意の値を返すために使用され、DML操作は関数では実行できません。関数には、スカラーとテーブル値の2つのタイプがあります。スカラー関数は単一の値を返し、テーブル値の関数はテーブルの行を返すために使用されます。


これは非常に古い質問で、多数の回答が含まれています。多くの回答(承認された回答を含む)は非常に賛成です。そのようなスレッドに別の回答を追加する前に、「別の回答を書く必要がある既存の回答に欠けているものは何ですか?」と
APC、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.