Nosso aplicativo precisa funcionar igualmente bem com um banco de dados Oracle ou Microsoft SQL Server. Para facilitar isso, criamos um punhado de UDFs para homogeneizar nossa sintaxe de consulta. Por exemplo, o SQL Server possui GETDATE () e o Oracle possui SYSDATE. Eles executam a mesma função, mas são palavras diferentes. Escrevemos um wrapper UDF chamado NOW () para ambas as plataformas que agrupa a sintaxe específica da plataforma relevante em um nome de função comum. Temos outras funções desse tipo, algumas das quais nada fazem senão que existem apenas em prol da homogeneização. Infelizmente, isso tem um custo para o SQL Server. UDFs escalares em linha causam estragos no desempenho e desabilitam completamente o paralelismo. Como alternativa, escrevemos funções de montagem CLR para atingir os mesmos objetivos. Quando implantamos isso em um cliente, eles começaram a enfrentar conflitos frequentes. Esse cliente em particular está usando técnicas de replicação e alta disponibilidade e estou me perguntando se há algum tipo de interação acontecendo aqui. Só não entendo como a introdução de uma função CLR causaria problemas como este. Para referência, incluí a definição UDF escalar original, bem como a definição CLR de substituição no C # e a declaração SQL. Também tenho XML de impasse que posso fornecer, se isso ajudar.
UDF original
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
Função de montagem CLR
[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
return value?.Trim();
}
Declaração do SQL Server para função CLR
CREATE FUNCTION [fn].[APAD]
(
@Value NVARCHAR(4000),
@TableName NVARCHAR(4000),
@ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
fonte
Respostas:
Quais versões do SQL Server você está usando?
Lembro-me de ter visto uma ligeira mudança de comportamento no SQL Server 2017 há não muito tempo. Vou ter que voltar e ver se consigo descobrir onde anotei isso, mas acho que tinha a ver com um bloqueio de esquema sendo iniciado quando um objeto SQLCLR estava sendo acessado.
Enquanto procuro isso, direi o seguinte sobre sua abordagem:
Sql*
tipos para parâmetros de entrada, tipos de retorno. Você deveria estar usando emSqlString
vez destring
.SqlString
é muito semelhante a uma corda anulável (o seuvalue?
, mas tem outra funcionalidade construído em que é SQL Server específicos. Todos osSql*
tipos têm umaValue
propriedade que retorna o tipo .NET esperado (por exemplo,SqlString.Value
retornosstring
,SqlInt32
retornosint
,SqlDateTime
retornosDateTime
, etc).Eu recomendaria contra toda essa abordagem, se os impasses estão relacionados ou não. Eu digo isso porque:
VARCHAR
. Você concorda com a conversão implícita de tudoNVARCHAR
e depois novamenteVARCHAR
para operações simples?NVARCHAR(4000)
eNVARCHAR(MAX)
: oMAX
tipo (com um único na assinatura) faz com que a chamada SQLCLR demore o dobro do tempo sem ter nenhumMAX
tipo na assinatura (acredito que isso ocorra verdadeiro paraVARBINARY(MAX)
vsVARBINARY(4000)
também). Então, você precisa decidir entre:NVARCHAR(MAX)
para ter uma API simplificada, mas obtenha o desempenho atingido quando você estiver usando 8000 bytes ou menos de dados de sequência ouMAX
tipos e uma sem (para quando você garante que nunca ultrapassará 8000 bytes de dados de string dentro ou fora). Essa é a abordagem que eu escolhi para a maioria das funções na minha biblioteca SQL # : existe umaTrim()
função que provavelmente possui um ou maisMAX
tipos e umaTrim4k()
versão que nunca possui umMAX
tipo em nenhum lugar do esquema da assinatura ou do conjunto de resultados. As versões "4k" são absolutamente mais eficientes.Você não está sendo cuidadoso em emular a funcionalidade, conforme o exemplo da pergunta.
LTRIM
eRTRIM
apenas aparar espaços, enquanto o .NETString.Trim()
apara os espaços em branco (pelo menos espaço, guias e novas linhas). Por exemplo:fonte