Mam ten mały CLR, który wykonuje funkcję RegEX na ciąg znaków w kolumnach.
Podczas uruchamiania w programie SQL Server 2014 (12.0.2000) w systemie Windows Server 2012R2 proces ulega awarii
Wiadomość 0, poziom 11, stan 0, wiersz 0 Wystąpił poważny błąd w bieżącym poleceniu. Ewentualne wyniki należy odrzucić.
i daje zrzut stosu, jeśli to zrobię
select count (*) from table where (CLRREGEX,'Regex')
ale kiedy robię
select * from table where (CLRREGEX,'Regex')
zwraca wiersze.
Działa doskonale na tej samej kompilacji SQL Server działającej w systemie Windows 8.1.
Jakieś pomysły?
- Edycja To jest tak proste, jak to tylko możliwe
using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlTypes; //SqlString, SqlInt32, SqlBoolean
using System.Text.RegularExpressions; //Match, Regex
using Microsoft.SqlServer.Server; //SqlFunctionAttribute
public partial class UserDefinedFunctions
{
public static readonly RegexOptions Options = RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline;
[SqlFunction]
[Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlBoolean RegExMatch(SqlString input, SqlString pattern)
{
if (input.IsNull || pattern.IsNull) //nulls dont qualify for a match
return SqlBoolean.False;
return Regex.IsMatch(input.Value, pattern.Value, RegexOptions.IgnoreCase);
}
}
Tak więc po drobnych zmianach działa to teraz: główna lekcja w języku C # wydaje się być taka sama jak w TSQL, strzeż się niejawnej konwersji danych.
using System;
using System.Text;
using System.Data.SqlTypes; //SqlString, SqlInt32, SqlBoolean
using System.Text.RegularExpressions; //Match, Regex
using Microsoft.SqlServer.Server; //SqlFunctionAttribute
public partial class UserDefinedFunctions
{
public static readonly RegexOptions Options = RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
[Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true, DataAccess = DataAccessKind.Read)]
public static SqlBoolean RegExMatch(SqlString input, SqlString pattern)
{
if (input.IsNull || pattern.IsNull) //nulls dont qualify for a match
return SqlBoolean.False;
string sqldata = input.ToString();
string regex = pattern.ToString();
return Regex.IsMatch(sqldata, regex);
}
sql-server
sql-server-2014
sql-clr
Spörri
źródło
źródło
SqlFunction
metoda jest oznaczona jakoIsDeterministic=true
? Czy zespół jest oznaczony jakoSAFE
?[SqlFunction]
atrybutu. Czy to dokładnie kod? Nie sądzę, żeby to się skompilowało. Rozróżnienie Framework w wersji 2.0 / 3.0 / 3.5 nie stanowi problemu, ponieważ używasz wersji 4.0 / 4.5 / 4.5.x / etc lub cokolwiek, co znajduje się na tym serwerze, ponieważ korzystasz z SQL Server 2014, który jest powiązany z wersją 4. CLR serwer pokazuje problem w wersji 32-bitowej? Ile pamięci ma w porównaniu do innych serwerów? I czy sprawdziłeś dzienniki SQL Server zaraz po otrzymaniu tego błędu?MatchTimeout
właściwości. Ale nie sądzę, że to jest tak naprawdę problem, jeśli podajesz maksymalnie 5 znaków. To jest możliwe, że jedna maszyna ma uszkodzone zainstalować .NET Framework, i które mogą być naprawione raz pstrąg działalność połowowa przestały ;-). Ponadto[0-9].*
jest prosty, ale również nieefektywny, ponieważ pasuje do wszystkich znaków, jeśli występują, po pierwszej cyfrze; używanie tylko[0-9]
dlaIsMatch
jest lepsze.DataAccessKind
naRead
? To tylko spowalnia i nie masz dostępu do danych. Zdaję sobie również sprawę, że wydaje się, że teraz działa, ale byłbym ostrożny z użyciem tejToString()
metodyValue
zamiast właściwości, ponieważ nie sądzę, aby ToString poprawnie obsługiwał kodowanie lub coś w tym rodzaju. Jakie jest ustawienie sortowania baz danych? Oczywiście, po prostu ponownie przeczytałem jeden z twoich komentarzy powyżej i widzę, że kolumna to VARCHAR zamiast NVARCHAR. Czy to pole ma inne zestawienie niż baza danych?Odpowiedzi:
Problemem jest konflikt ustawień regionalnych między systemem operacyjnym Windows a SQL Server (w szczególności bazą danych, w której zestaw jest ładowany). Możesz uruchomić następujące zapytanie, aby zobaczyć, na co oba są ustawione:
Jeśli są różne, na pewno możesz uzyskać pewne „dziwne” zachowanie, takie jak to, co widzisz. Problem polega na tym, że:
SqlString
zawiera więcej niż sam tekst: obejmuje domyślne sortowanie bazy danych, w której istnieje zestaw. Zestawienie składa się z dwóch części informacji: informacji o lokalizacji (tj. LCID) oraz opcji porównania (tj. SqlCompareOptions), które wyszczególniają wrażliwość na wielkość liter, akcenty, kana, szerokość lub wszystko (binarne i binarne2).Konflikt zwykle występuje, gdy odwołuje się do parametru SqlString bez użycia
.Value
lub w.ToString()
taki sposób, że dokonuje niejawnej konwersji naSqlString
. W takim przypadku spowodowałoby to wyjątek stwierdzający, że LCID nie są zgodne.Są najwyraźniej inne scenariusze, takie jak przeprowadzanie porównań ciągów (niektóre / wszystkie?), W tym przy użyciu Regex, jak pokazuje ten przypadek (chociaż do tej pory nie byłem w stanie tego odtworzyć).
Kilka pomysłów na poprawki:
Idealny (zawsze będą spełnione oczekiwania dotyczące działania porównań):
Mniej niż idealny (zachowanie ustawień regionalnych Windows może nie być tymi samymi regułami dla równości i sortowania, więc mogą wystąpić nieoczekiwane wyniki):
.ToString
metody lub.Value
właściwości, które zwracają ciąg bez identyfikatora LCID programu SQL Server, aby wszystkie operacje korzystały z identyfikatora LCID systemu operacyjnego.Może pomóc:
SqlChars
zamiast,SqlString
ponieważ nie przynosi LCID i informacji o sortowaniu z SQL ServerStringComparison.InvariantCulture
:String.Compare(string, string, StringComparison.InvariantCulture)
lubString.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
RegexOptions.CultureInvariant
źródło
Zaktualizowano ..
Lokalizacja różni się między silnikiem SQL a serwerem okien, jak wskazuje @srutzky:
Następująca zmiana w kodzie - ustawienie opcji
RegexOptions.CultureInvariant
omija błąd. Niezmieniony kod nie spowoduje awarii programu SQL Server 2012 w systemie Windows Server 2012R2 z tymi samymi ustawieniami języka, ale dzieje się tak w przypadku programu SQL Server 2014.źródło
SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;
. Całkiem możliwe, że problemem był konflikt w ustawieniach językowych. Twoje rozwiązanie może nadal być najlepszym rozwiązaniem, ale generalnie nie powinno być potrzeby używaniaToString()
zamiastValue
właściwości naSqlString
s. Byłoby więc miło potwierdzić sytuację.RegexOptions.CultureInvariant
ponieważ nie przekazujeszOptions
zmiennejRegex.IsMatch(sqldata, regex)
. Rzecz, która zmieniła się pomiędzy oryginalnym kodem i nowego kodeksu pracy, to poszedł z użyciemSqlString.Value
doSqlString.ToString()
. Podejrzewam, że zobaczyłbyś to samo stałe zachowanie, gdybyś przestawił się na używanieSqlChars
. Ale zrobiłbym to tylko jako test. Najlepszym rozwiązaniem jest zmiana identyfikatora LCID systemu Windows lub SQL Server, aby pasował do drugiego. Możesz także usunąć zmienną statyczną Opcje..Value
właściwością ,SqlString
ponieważ najwyraźniej zwraca tę samą wartość wewnętrzną co.ToString()
metoda. Nadal badam i zaktualizuję swoją odpowiedź tym, co znajdę :).RegexOptions.IgnoreCase
a drugi nie. Skonfigurowałem podobne środowisko: Windows (8.0) przy użyciu LCID 1033, SQL Server DB ma LCID 1039, przy użyciu tego samego RegEx, który napisałeś, robiącCOUNT(*)
naVARCHAR
polu wypełnionym GUID, używając wzorca'[0-3â].*'
, na stole z 10 milionami rzędów. To SQL Server 2012, a nie 2014, choć nie uważam, że to powinno mieć znaczenie.