私たちのアプリケーションは、OracleデータベースまたはMicrosoft SQL Serverデータベースと同等にうまく機能する必要があります。これを容易にするために、クエリ構文を均質化する少数のUDFを作成しました。たとえば、SQL ServerにはGETDATE()があり、OracleにはSYSDATEがあります。それらは同じ機能を実行しますが、それらは異なる単語です。共通の関数名で関連するプラットフォーム固有の構文をラップする、両方のプラットフォーム用のNOW()と呼ばれるラッパーUDFを作成しました。他にもそのような機能があり、その一部は本質的に均質化のためにのみ存在するものです。残念ながら、これにはSQL Serverのコストがかかります。インラインスカラーUDFはパフォーマンスに大きな影響を与え、並列処理を完全に無効にします。別の方法として、同じ目標を達成するためにCLRアセンブリ関数を作成しました。これをクライアントに展開すると、デッドロックが頻繁に発生し始めました。この特定のクライアントはレプリケーションと高可用性の技術を使用しており、ここで何らかの相互作用が行われているのではないかと思っています。 CLR関数を導入すると、このような問題がどのように発生するのか理解できません。参考までに、元のスカラーUDF定義と、C#の置換CLR定義およびそのSQL宣言を含めました。また、それが役立つ場合に提供できるデッドロックXMLもあります。
元のUDF
CREATE FUNCTION [fn].[APAD]
(
@Value VARCHAR(4000)
, @tablename VARCHAR(4000) = NULL
, @columnname VARCHAR(4000) = NULL
)
RETURNS VARCHAR(4000)
WITH SCHEMABINDING
AS
BEGIN
RETURN LTRIM(RTRIM(@Value))
END
GO
CLRアセンブリ関数
[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
return value?.Trim();
}
CLR関数のSQL Server宣言
CREATE FUNCTION [fn].[APAD]
(
@Value NVARCHAR(4000),
@TableName NVARCHAR(4000),
@ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
SQL Serverのどのバージョンを使用していますか?
SQL Server 2017で動作が少し変わったのを覚えていますか。戻って、どこにメモしたかを見つける必要があるかどうかを確認する必要がありますが、SQLCLRオブジェクトがアクセスされているときにスキーマロックが開始されたことが原因であると思います。
私がそれを探している間、私はあなたのアプローチに関して次のことを言います:
Sql*
_型を使用してください。 SqlString
ではなくstring
を使用する必要があります。 SqlString
はnull可能文字列(お使いの_value?
_)によく似ていますが、SQL Server固有のその他の機能が組み込まれています。すべての_Sql*
_タイプには、予期される.NETタイプを返すValue
プロパティがあります(たとえば、_SqlString.Value
_はstring
を返し、_SqlInt32
_はint
を返し、SqlDateTime
はDateTime
を返しますなど)。デッドロックが関係しているかどうかにかかわらず、まずこのアプローチ全体に反対することをお勧めします。私はこれをこう言います:
VARCHAR
を許可しません。すべてを暗黙的にNVARCHAR
に変換してから、再びVARCHAR
に戻して簡単な操作を行うことはできますか?NVARCHAR(4000)
とNVARCHAR(MAX)
の間には大きな違いがあります。MAX
タイプ(シグネチャに1つでもある)は、SQLCLR呼び出しを2回行いますシグネチャにMAX
タイプがない限り(これはVARBINARY(MAX)
とVARBINARY(4000)
にも当てはまると思います)。したがって、次のいずれかを決定する必要があります。NVARCHAR(MAX)
のみを使用してAPIを簡略化しますが、8000バイト以下の文字列データを使用している場合はパフォーマンスに影響します。またはMAX
型、もう1つはありません(8000バイトを超える文字列データが出入りしないことが保証されている場合)。これは、私の SQL# ライブラリ内のほとんどの関数に対して選択したアプローチです。Trim()
関数が1つ以上のMAX
タイプとTrim4k()
バージョン。これは、シグネチャまたは結果セットスキーマのどこにもMAX
タイプを持たないものです。 「4k」バージョンは絶対に効率的です。問題の例で与えられた機能をエミュレートするように注意していない。 LTRIM
およびRTRIM
はスペースのみをトリムしますが、.NET String.Trim()
は空白(少なくともスペース、タブ、および改行)をトリムします。例えば:
_ PRINT LTRIM(RTRIM(N' a '));
_