web-dev-qa-db-ja.com

CLR関数の入力パラメーターにSqlStringsの代わりに文字列を使用しても安全ですか?

C#コードで実装されたCLRスカラーUDFを持っています。入力パラメーターにStringデータ型を使用すると、SqlStringデータ型に比べてパフォーマンスが大幅に向上することに気付きました。 SQLCLRレベル5への階段:開発(SQL Server内での.NETの使用) では、 Solomon Rutzky は、文字列にSQLデータ型を好む次の理由について述べています。

ネイティブ共通言語ランタイム(CLR)データ型とSQL Serverデータ型の主な違いは、前者はNULL値を許可しないが、後者は完全なNULLセマンティクスを提供することです。

...

のストリーミング値は、N [VAR] CHARの場合はSqlChars、[VAR] BINARYの場合はSqlBytes、XMLの場合はSqlXml.CreateReader()を使用して実現できます。

...

SqlString(文字列やSqlCharsではない)を使用する場合、CompareInfo、CultureInfo、LCID、およびSqlCompareOptionsプロパティにアクセスできます...

入力がNULLになることはなく、値をストリームインする必要もありません。また、照合プロパティをチェックすることもありません。私の場合は、Stringの代わりにSqlStringを使用するほうがよい例外でしょうか?そのやり方をすれば、特に気をつけるべきことはありますか?

問題がある場合は、SQL Serverの既定の照合順序を使用しています。これは私のソースコードの一部であり、s1は入力パラメーターです。

fixed (char* chptr = s1)
{
    char* cp = (char*)current;

    for (int i = 0; i < s1.Length; i++)
    {
        cp[i] = chptr[i];
    }
}
6
Joe Obbish

すばらしい質問です。私の知る限り、これらの条件(つまり、NULLsがなく、追加の機能が必要ないことが保証されている)のもとでは、特に心配する必要はありません。これはCURSORsに似た状況であり、一般的なルールが必要な場合は、「カーソルを使用しない」です。ただし、実際のルールは、「適切なときに/場所でのみカーソルを使用する」です。問題は、カーソルの技術的な詳細について人々に教育して、彼らが決定を下せるようにすることです。そのようなことについて十分に知っている私たちは、一般的なルールを無視し、適切に使用します。

したがって、混乱とエラーを減らすため、「常に」_Sql*_型を使用することをお勧めします。しかし、それはあなたの状況でstringを使用するのが良くないということではありません。私はそれのために行くと言い、stringで問題が発生した場合は、戻ってSqlStringに変更するのは簡単です。

照合とあなたの声明に関して:

問題がある場合は、SQL Serverの既定の照合順序を使用しています。

これは一般に問題ではありませんが、真のデフォルト照合がないことを考えると、ここで何を意味するかも少し不明確です。言語設定が「US English」のOSにSQL Serverをインストールするときに、 不運なデフォルト照合を参照している可能性があります (つまり、LCID = 1033)、つまり_SQL_Latin1_General_CP1_CI_AS_。ただし、すべて異なる3つのレベルの照合順序(インスタンス/サーバー、データベース、および列)がまだあり、これらのレベルの1つまたは2つだけを意味している場合があります。

私がこれすべてについて言及する理由は、いくつかの非自明なことがここで起こっているということです:

  1. sQLCLRスレッドの既定のカルチャはOSレベルの言語設定(選択した言語のLCID)であるため、照合が影響するこれらの3つのレベルはある程度関係ありません。これは、2つの_String.Equals_値のいずれかを使用する場合は_StringComparison.CurrentCulture*_を使用する操作に影響し、カルチャを指定しない場合は_String.Compare_を使用する操作に影響します。

  2. _=_演算子は序数比較を行うため(つまり、__BIN2_照合順序を使用する場合と同じである必要があるため)、照合順序が影響を与えるこれら3つのレベルはある程度関係ありません。これは_String.CompareOrdinal_の動作方法でもあります。notが_String.Equals_または_StringComparison.CurrentCulture*_の値を渡す場合の_StringComparison.InvariantCulture*_も同様です。

  3. sQL Server照合が重要な1つの例は、SqlString入力パラメーターを_+_を介してstringと連結する場合です。この場合、_+_演算子は、SqlStringの値を含む新しいstringを作成して、2つのSqlStringsを連結できるようにします。問題は、新しいSqlStringが現在のスレッドLCID(オペレーティングシステムのLCID)で作成され、次に_+_演算子が連結の前に2つのSqlStringsを比較することです(つまり、それらが「同じタイプ」であることを確認します。ただし、SqlString入力パラメーターにdatabase(インスタンスまたは列ではない)のLCIDがあり、暗黙的に作成されたSqlStringがあるため、 OSのLCIDの場合、操作は「照合順序」が一致しないことを示す例外を受け取ります。いいですね

    ただし、文字列が必要なときにSqlStringの値を直接使用する必要はないため、これはshouldの問題ではありません。代わりに、全員が常にValueプロパティを使用して文字列を取得する必要があります。


とはいえ、stringの方が高速であると判断するためにどのようなテストを行ったかについて知りたいと思います。単一のNVARCHAR(4000)入力パラメーターを受け入れ、短い文字列を連結してから新しい値を返す単純なUDFをテストしました。そのUDFの1つのバージョンはstringを受け入れて返し、もう1つのバージョンはSqlStringを受け入れて返します。 100万回を超えるstringバージョンはSqlStringバージョンよりも約200〜300ミリ秒高速であり、最速の時間(100万回の繰り返しではなく、それぞれ)。残りの50%の時間では、パフォーマンスの向上は約100ミリ秒でしたが、まったくない場合もあります。

また、テストコードに関しては、stringであろうとSqlStringであろうと、_s1_は常に直接入力パラメーターですか?はいの場合、ローカルで文字列を作成し、それを_s1.Value_に設定することもテストする必要があります。意味:

_string s2 = s1.Value; // when s1 is SqlString instead of string

fixed (char* chptr = s2)
{
    char* cp = (char*)current;

    for (int i = 0; i < s2.Length; i++)
    {
        cp[i] = chptr[i];
    }
}
_

また、おそらくテストする他のいくつかのオプション:

  1. SqlString.GetUnicodeBytes メソッド(_byte[]_を返します)
  2. SqlChars.Value プロパティ(_char[]_を返します)
6
Solomon Rutzky