Nuestra aplicación debe funcionar igualmente bien con una base de datos Oracle o una base de datos Microsoft SQL Server. Para facilitar esto, creamos un puñado de UDF para homogeneizar nuestra sintaxis de consulta. Por ejemplo, SQL Server tiene GETDATE () y Oracle tiene SYSDATE. Realizan la misma función pero son palabras diferentes. Escribimos un UDF contenedor llamado NOW () para ambas plataformas que envuelve la sintaxis específica de la plataforma relevante en un nombre de función común. Tenemos otras funciones similares, algunas de las cuales esencialmente no hacen más que existir únicamente por el bien de la homogeneización. Desafortunadamente, esto tiene un costo para SQL Server. Las UDF escalares en línea causan estragos en el rendimiento y deshabilitan completamente el paralelismo. Como alternativa, escribimos funciones de ensamblaje CLR para lograr los mismos objetivos. Cuando implementamos esto en un cliente, comenzaron a experimentar puntos muertos frecuentes. Este cliente en particular está utilizando técnicas de replicación y alta disponibilidad y me pregunto si hay algún tipo de interacción aquí. Simplemente no entiendo cómo la introducción de una función CLR podría causar problemas como este. Como referencia, he incluido la definición original de UDF escalar, así como la definición de reemplazo de CLR en C # y la declaración de SQL correspondiente. También tengo un XML de punto muerto que puedo proporcionar si eso ayuda.
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
Función de ensamblaje de CLR
[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
return value?.Trim();
}
Declaración de SQL Server para la función CLR
CREATE FUNCTION [fn].[APAD]
(
@Value NVARCHAR(4000),
@TableName NVARCHAR(4000),
@ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
fuente
Respuestas:
¿Qué versión (s) de SQL Server está utilizando?
Recuerdo haber visto un ligero cambio en el comportamiento en SQL Server 2017 no hace mucho tiempo. Tendré que volver y ver si puedo encontrar dónde lo anoté, pero creo que tenía que ver con un bloqueo de esquema que se iniciaba cuando se accedía a un objeto SQLCLR.
Mientras estoy buscando eso, diré lo siguiente con respecto a su enfoque:
Sql*
tipos para los parámetros de entrada, tipos de retorno. Deberías estar usando enSqlString
lugar destring
.SqlString
es muy similar a una cadena anulable (tuvalue?
, pero tiene otra funcionalidad incorporada en que es SQL Server específico. Todos losSql*
tipos tienen unaValue
propiedad que devuelve el tipo .NET esperada (por ejemplo,SqlString.Value
vuelvestring
,SqlInt32
vuelveint
,SqlDateTime
vuelveDateTime
, etc).Para empezar, recomendaría contra todo este enfoque, independientemente de si los puntos muertos están relacionados o no. Digo esto porque:
VARCHAR
. ¿Estás de acuerdo con convertir implícitamente todoNVARCHAR
y luego nuevamenteVARCHAR
para operaciones simples?NVARCHAR(4000)
yNVARCHAR(MAX)
: elMAX
tipo (que tiene incluso uno solo en la firma) hace que la llamada SQLCLR tome el doble de tiempo que no tener ningúnMAX
tipo en la firma (creo que esto se cumple cierto paraVARBINARY(MAX)
vsVARBINARY(4000)
también). Por lo tanto, debe decidir entre:NVARCHAR(MAX)
para tener una API simplificada, pero tome el impacto en el rendimiento cuando use 8000 bytes o menos de datos de cadena, oMAX
tipos y otra sin ellas (para cuando se garantiza que nunca ingrese o salga más de 8000 bytes de datos de cadena). Este es el enfoque que elegí para la mayoría de las funciones en mi biblioteca SQL # : hay unaTrim()
función que probablemente tiene uno o másMAX
tipos, y unaTrim4k()
versión que nunca tiene unMAX
tipo en ninguna parte del esquema de firma o conjunto de resultados. Las versiones "4k" son absolutamente más eficientes.No tiene cuidado de emular la funcionalidad dado el ejemplo en la pregunta.
LTRIM
yRTRIM
solo recortar espacios, mientras que .NETString.Trim()
recorta el espacio en blanco (al menos espacio, pestañas y líneas nuevas). Por ejemplo:fuente