TVPを読み取り専用にする必要がある理由、および他のタイプのパラメーターを読み取り専用にできない理由


19

このブログによると、関数またはストアドプロシージャのOUTPUTパラメーターは、パラメーターでない場合は本質的に値渡しであり、パラメーターである場合は本質的に参照渡しの安全なバージョンとして扱われOUTPUTます。

最初は、TVPの宣言を強制する目標は、TVPをパラメーターREADONLYとして使用できないことを開発者に明確に知らせることであると考えましたが、OUTPUT非TVPをとして宣言できないため、さらに処理が必要READONLYです。たとえば、次は失敗します。

create procedure [dbo].[test]
@a int readonly
as
    select @a

メッセージ346、レベル15、状態1、プロシージャテスト
パラメーター "@a"はテーブル値パラメーターではないため、READONLYとして宣言できません。

  1. 以来統計が保存されていない TVPにDML操作を防止することの理論的根拠は何ですか?
  2. OUTPUT何らかの理由でTVPをパラメーターにしたくないことに関連していますか?

回答:


19

説明は、a)この質問で言及されていないリンクされたブログの詳細、b)常にパラメーターの受け渡し方法に適合するTVPの語用論、c)および自然の組み合わせに結びついているようです。テーブル変数の。

  1. リンクされたブログ投稿に含まれていない詳細は、ストアドプロシージャとストアドプロシージャで変数がどのように渡されるかということです(これは、「OUTPUTパラメータの場合、参照渡しのより安全なバージョン」の質問のフレージングに関連しています) :

    TSQLは、コピーイン/コピーアウトセマンティクスを使用して、ストアドプロシージャと関数にパラメーターを渡します。

    ...ストアドプロシージャの実行が(エラーをヒットすることなく)終了すると、コピーアウトが行われ、ストアドプロシージャで行われた変更で渡されたパラメータが更新されます。

    このアプローチの本当の利点はエラーの場合です。ストアドプロシージャの実行中にエラーが発生した場合、パラメーターに加えられた変更は呼び出し元に反映されません。

    OUTPUTキーワードが存在しない場合、コピーアウトは行われません。

    一番下の行:
    ストアドプロシージャのパラメーターは、エラーが発生した場合、ストアドプロシージャの部分的な実行を決して反映しません。

    このパズルのパート1は、パラメーターが常に「値で」渡されることです。そして、それはパラメータとしてマークされている場合のみであるOUTPUT ストアドプロシージャは、現在の値が実際に送り返されることを正常に完了します。OUTPUT値が真に「参照によって」渡された場合、その変数のメモリ内の場所へのポインタは、値そのものではなく、渡されたものになります。ポインタ(メモリアドレスなど)を渡すと、ストアドプロシージャの次の行でエラーが発生して実行が中止された場合でも、加えられた変更はすぐに反映されます。

    パート1を要約すると、変数値は常にコピーされます。それらはメモリアドレスによって参照されません。

  2. パート1を念頭に置いて、渡される変数が非常に大きい場合、変数値を常にコピーするというポリシーはリソースの問題につながる可能性があります。私は(BLOB型がどのように処理されるか確認するためにテストしていませんVARCHAR(MAX)NVARCHAR(MAX)VARBINARY(MAX)XML、そしてもう使用すべきではないもの:TEXTNTEXT、およびIMAGE)が、渡されるデータの任意のテーブルが非常に大きくなることができると言うことは安全です。TVP機能を開発している人にとって、クールな新機能が健全な数のシステムを破壊しないようにする(つまり、よりスケーラブルなアプローチが必要な)真の「参照渡し」機能を望むのは理にかなっています。あなたが彼らがやったことであるドキュメントで見ることができるように:

    Transact-SQLは、テーブル値パラメーターを参照によってルーチンに渡し、入力データのコピーを作成しないようにします。

    また、このメモリ管理の問題は、SQL Server 2005で導入されたSQLCLR API(TVPはSQL Server 2008で導入された)に見られるため、新しい概念ではありませんでした。渡すときNVARCHARVARBINARY(SQLCLRアセンブリ内の.NETメソッドにすなわち入力パラメータを)SQLCLRコードにデータを、あなたはどちらか使用して、「値で」アプローチで行くためのオプション持っているSqlStringか、SqlBinaryそれぞれのか、「参照して行くことができますどちらかSqlCharsまたはSqlBytesそれぞれを使用してアプローチします。種類が値(2ギガバイト、右まで)全体の200メガバイトをコピーするとは対照的に、あなたは大きな値の小さな塊を引っ張ることができるような.NET CLRへのデータのフルストリーミングを可能にします。SqlCharsSqlBytes

    パート2を要約すると、TVPは、その性質上、「常に値をコピーする」モデル内にとどまると、多くのメモリを消費する(したがってパフォーマンスが低下する)傾向があります。したがって、TVPは真の「参照渡し」を行います。

  3. 最後の部分は、パート2が重要な理由です。TVPのコピーを作成するのではなく、本当に「参照によって」渡すことで何が変わるのでしょうか。そして、それは、パート1の基礎となる設計目標によって答えられます。正常に完了しないストアドプロシージャは、マークされているかどうかにかかわらず、入力パラメーターを変更しOUTPUTないでください。DML操作を許可すると、呼び出しコンテキストに存在するTVPの値に即座に影響します(参照渡しは、渡されたもののコピーではなく、渡されたものを変更することを意味するため)。

    現在、誰かがこの時点でおそらくモニターに話しかけています。「まあ、ストアドプロシージャに渡された場合、TVPパラメーターに加えられた変更をロールバックするための自動マジック機能を組み込んでください。問題は解決しました。」そんなに早くない。これがテーブル変数の性質の出番です。テーブル変数に加えられた変更はトランザクションによってバインドされません!そのため、変更をロールバックする方法はありません。そして実際、これはロールバックが必要な場合にトランザクション内で生成された情報を保存するために使用されるトリックです:-)。

    パート3をまとめると、テーブル変数は、ストアドプロシージャを中止させるエラーが発生した場合に、それらに対して行われた変更を「元に戻す」ことを許可しません。また、これは、部分実行を反映しないパラメーターを持つという設計目標に違反します(パート1)。

エルゴは:READONLYキーワードは、それらが実際には「参照によって」渡される表変数であるのでTVPsにDML操作を防止するために必要とされない、したがってそれらのすべての変更がすぐに反映される、ストアドプロシージャでエラーが発生した場合であっても、無ありますそれを防ぐ他の方法。

さらに、他のデータ型のパラメーターは、READONLY渡されたもののコピーであるため使用できません。したがって、まだ保護されていないものは保護されません。それと、他のデータ型のパラメーターが機能する方法は読み取り/書き込みを目的としていたため、そのAPIを変更して読み取り専用の概念を含めるようにすると、さらに作業が増えるでしょう。


非常に詳細な説明。ありがとう。だから、ストアドプロシージャで渡されたテーブル変数(ユーザーTVP TYPE変数またはDECLARE x as TABLE (...))を変更する方法はありませんか?set @tvp = myfunction(@tvp)関数のRETURNS値がTVPタイプと同じDDLのテーブルである場合、代わりに関数を使用して、メモリフットプリントが大きくても、それを行うことはできますか?
mpag

@mpagありがとう。TVP テーブル変数であり、違いはありません。タイプを渡すのではなく、タイプまたは明示的なスキーマ宣言から作成されたテーブル変数を渡します。また、SET少なくとも私が知っている限りでは、テーブル変数を使用することはできません。また、可能であれば:a)=演算子を介して結果セットにアクセスできず、b)TVPがまだとしてマークされているREADONLYため、設定に違反することになります。一時テーブルまたはプロシージャ内で作成した別のテーブル変数に内容をダンプするだけです。
ソロモンラッツキー

再度、感謝します。基本的に一時テーブルアプローチを使用することにしました。
mpag

5

Martin Smithによる質問へのコメントから生成されたコミュニティWikiの回答

これには、アクティブなConnectアイテム(Erland Sommarskogが送信)があります。

SPが相互に呼び出す場合、テーブルパラメータは読み取り専用でなければならないという制限を緩和します。

マイクロソフトによるこれまでの唯一の回答では、次のように述べています(強調を追加)。

これについてのフィードバックをありがとう。多くのお客様から同様のフィードバックをいただいています。テーブル値パラメーターの読み取り/書き込みを許可するには、SQLエンジン側とクライアントプロトコルでかなりの作業が必要です。時間/リソースの制約およびその他の優先事項のため、SQL Server 2008リリースの一部としてこの作業を行うことはできません。ただし、この問題を調査し、次のSQL Serverのリリースの一部として対処するためにレーダーでこれをしっかりと把握しています。ここでフィードバックを歓迎します。

Srini Acharya
シニアプログラムマネージャー
SQL Server Relational Engine

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.