Nasza aplikacja musi równie dobrze współpracować z bazą danych Oracle lub Microsoft SQL Server. Aby to ułatwić, stworzyliśmy garść UDF w celu ujednolicenia naszej składni zapytań. Na przykład SQL Server ma GETDATE (), a Oracle ma SYSDATE. Pełnią tę samą funkcję, ale są różnymi słowami. Napisaliśmy opakowanie UDF o nazwie NOW () dla obu platform, które otacza odpowiednią składnię specyficzną dla platformy wspólną nazwą funkcji. Mamy inne takie funkcje, z których niektóre w zasadzie nic nie istnieją, ale istnieją wyłącznie w celu homogenizacji. Niestety ma to koszt dla SQL Server. Inline skalarne UDF sieją spustoszenie w wydajności i całkowicie wyłączają równoległość. Alternatywnie napisaliśmy funkcje asemblera CLR, aby osiągnąć te same cele. Kiedy wdrożyliśmy to na kliencie, zaczęli doświadczać częstych impasów. Ten konkretny klient korzysta z technik replikacji i wysokiej dostępności i zastanawiam się, czy nie zachodzi tutaj jakaś interakcja. Po prostu nie rozumiem, w jaki sposób wprowadzenie funkcji CLR spowodowałoby takie problemy. Dla odniesienia załączyłem oryginalną skalarną definicję UDF, a także zastępczą definicję CLR w C # i deklarację SQL dla niej. Mam także zakleszczenie XML, które mogę podać, jeśli to pomoże.
Oryginalny 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
Funkcja montażu CLR
[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
return value?.Trim();
}
Deklaracja programu SQL Server dla funkcji CLR
CREATE FUNCTION [fn].[APAD]
(
@Value NVARCHAR(4000),
@TableName NVARCHAR(4000),
@ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
źródło
Odpowiedzi:
Z jakich wersji programu SQL Server korzystasz?
Pamiętam niewielką zmianę zachowania w SQL Server 2017 nie tak dawno temu. Będę musiał wrócić i sprawdzić, czy mogę znaleźć miejsce, w którym to zanotowałem, ale myślę, że miało to związek z zainicjowaniem blokady schematu podczas uzyskiwania dostępu do obiektu SQLCLR.
Podczas gdy tego szukam, powiem o twoim podejściu:
Sql*
typów dla parametrów wejściowych, typów zwrotów. Powinieneś używaćSqlString
zamiaststring
.SqlString
jest bardzo podobny do łańcucha zerowego (twójvalue?
, ale ma wbudowaną inną funkcjonalność, która jest specyficzna dla SQL Server. WszystkieSql*
typy mająValue
właściwość, która zwraca oczekiwany typ .NET (np.SqlString.Value
zwracastring
,SqlInt32
zwracaint
,SqlDateTime
zwracaDateTime
itp.).Odradzałbym na początku całe to podejście, niezależnie od tego, czy impasy są powiązane. Mówię to, ponieważ:
VARCHAR
. Czy zgadzasz się z niejawną konwersją wszystkiego na,NVARCHAR
a następnie z powrotemVARCHAR
na proste operacje?NVARCHAR(4000)
iNVARCHAR(MAX)
: theMAX
type (mając nawet jednego z nich w podpisie) dokonanie podjąć rozmowę SQLCLR dwa razy tak długo, jak nie mając żadnychMAX
typ w podpisie (wierzę to trzyma prawdziwe również dlaVARBINARY(MAX)
vsVARBINARY(4000)
). Musisz więc zdecydować między:NVARCHAR(MAX)
uproszczonego interfejsu API, ale skorzystaj z wydajności, jeśli używasz 8000 bajtów lub mniej danych ciągowych, lubMAX
typami, a drugą bez (na wypadek, gdy nigdy nie przekroczysz 8000 bajtów danych wejściowych lub wyjściowych). Takie podejście wybrałem dla większości funkcji w mojej bibliotece SQL # : istniejeTrim()
funkcja, która prawdopodobnie ma jeden lub więcejMAX
typów orazTrim4k()
wersję, która nigdy nie ma żadnegoMAX
typu w schemacie podpisu lub zestawu wyników. Wersje „4k” są absolutnie bardziej wydajne.Biorąc pod uwagę przykład w pytaniu, nie jesteś ostrożny, aby emulować funkcjonalność.
LTRIM
iRTRIM
tylko przycinaj spacje, a .NETString.Trim()
przycina białe spacje (przynajmniej spację, tabulatory i znaki nowej linii). Na przykład:źródło