Наше приложение должно одинаково хорошо работать с базой данных Oracle или базой данных Microsoft SQL Server. Чтобы облегчить это, мы создали несколько UDF для гомогенизации нашего синтаксиса запроса. Например, в SQL Server есть GETDATE (), а в Oracle - SYSDATE. Они выполняют одну и ту же функцию, но это разные слова. Мы написали UDF-оболочку с именем NOW () для обеих платформ, которая оборачивает специфический для конкретной платформы синтаксис в общее имя функции. У нас есть другие такие функции, некоторые из которых по сути ничего не делают, но существуют исключительно ради гомогенизации. К сожалению, это имеет цену для SQL Server. Встроенные скалярные UDF наносят ущерб производительности и полностью отключают параллелизм. В качестве альтернативы мы написали функции сборки CLR для достижения тех же целей. Когда мы развернули это на клиенте, они начали испытывать частые тупики. Этот конкретный клиент использует методы репликации и высокой доступности, и мне интересно, происходит ли какое-то взаимодействие здесь. Я просто не понимаю, как введение функции CLR может вызвать такие проблемы. Для справки я включил исходное скалярное определение UDF, а также определение CLR для замены в C # и объявление 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();
}
Декларация SQL Server для функции CLR
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
очень похож на пустую строку (вашуvalue?
, но в нее встроены другие функции, специфичные для SQL Server. У всехSql*
типов естьValue
свойство, которое возвращает ожидаемый тип .NET (например,SqlString.Value
returnstring
,SqlInt32
returnint
,SqlDateTime
returnDateTime
и т. д.).Я бы порекомендовал против всего этого подхода начать с того, связаны ли взаимоблокировки. Я говорю это потому что:
VARCHAR
. Вы в порядке с неявным преобразованием всего вNVARCHAR
и затем сноваVARCHAR
для простых операций?NVARCHAR(4000)
иNVARCHAR(MAX)
:MAX
тип (имеющий хотя бы один из них в сигнатуре) заставляет вызов SQLCLR занимать в два раза больше времени, чем отсутствие какого-либоMAX
типа в сигнатуре (я полагаю, что это верно верно иVARBINARY(MAX)
противVARBINARY(4000)
). Итак, вам нужно выбрать между:NVARCHAR(MAX)
для упрощенного API, но снизить производительность при использовании строковых данных 8000 байт или менее, илиMAX
типами, а другая без (для случаев, когда вы гарантированно никогда не будете передавать или выводить более 8000 байтов строковых данных). Именно этот подход я выбрал для большинства функций в моей библиотеке SQL # : естьTrim()
функция, которая, вероятно, имеет один или несколькоMAX
типов, иTrim4k()
версия, которая никогда не имеетMAX
типа где-либо в схеме сигнатуры или набора результатов. Версии "4k" абсолютно более эффективны.Вы не будете осторожны, чтобы эмулировать функциональность, учитывая пример из вопроса.
LTRIM
иRTRIM
только обрезать пробелы, в то время как .NETString.Trim()
обрезает пробелы (по крайней мере, пробел, табуляции и новые строки). Например:источник