У меня есть этот маленький CLR, который выполняет функцию RegEX для строки в столбцах.
При запуске на SQL Server 2014 (12.0.2000) в Windows Server 2012R2 процесс завершается с
Сообщение 0, уровень 11, состояние 0, строка 0 Произошла серьезная ошибка в текущей команде. Результаты, если таковые имеются, должны быть отброшены.
и дает дамп стека, если я делаю
select count (*) from table where (CLRREGEX,'Regex')
но когда я сделаю
select * from table where (CLRREGEX,'Regex')
он возвращает строки.
Отлично работает на той же сборке SQL Server, работающей на Windows 8.1.
Есть идеи?
- Редактировать Это так просто, как может быть
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);
}
}
Поэтому с небольшими изменениями это работает: основной урок в C # кажется таким же, как в TSQL, остерегайтесь неявного преобразования данных.
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
источник
источник
SqlFunction
метод помечен какIsDeterministic=true
? Помечена ли сборка какSAFE
?[SqlFunction]
атрибута duplicate . Это точный код? Я не думаю, что это скомпилируется. Различие в версии 2.0 / 3.0 / 3.5 платформы не является проблемой, поскольку вы используете 4.0 / 4.5 / 4.5.x / etc или что-то еще на этом сервере, так как вы используете SQL Server 2014, который привязан к CLR версии 4. Является ли сервер показывает проблему 32-бит? Сколько памяти у него по сравнению с другими серверами? И вы проверили журналы SQL Server сразу после получения этой ошибки?MatchTimeout
свойство. Но я не думаю, что это действительно проблема, если вы проходите не более 5 символов. Это является возможным , что это одна машина имеет поврежденную установку в .NET Framework, и которые могут быть восстановлены , как только форель промысловая деятельность перестала ;-). Кроме того,[0-9].*
это просто, но также неэффективно, поскольку оно соответствует всем символам, если они есть, после первой цифры; лучше использовать только[0-9]
дляIsMatch
.DataAccessKind
наRead
? Это только замедляет его, и вы не делаете никакого доступа к данным. Кроме того, я понимаю, что он, кажется, работает сейчас, но я буду осторожен с использованиемToString()
метода, а не соValue
свойством, так как не думаю, что ToString правильно обрабатывает кодировки или что-то в этом роде. Как настроено сопоставление баз данных? Конечно, я просто перечитал один из ваших комментариев выше и увидел, что столбец VARCHAR вместо NVARCHAR. Это поле имеет другое сопоставление, чем база данных?Ответы:
Проблема заключается в конфликте локалей между ОС Windows и SQL Server (в частности, базой данных, в которую загружается сборка). Вы можете выполнить следующий запрос, чтобы увидеть, на что они оба настроены:
Если они разные, то вы можете определенно получить «странное» поведение, например то, что вы видите. Проблема в том, что:
SqlString
включает в себя больше, чем просто текст: он включает сопоставление по умолчанию для базы данных, в которой существует сборка. Сортировка состоит из двух частей информации: информации о локали (например, LCID) и опций сравнения (например, SqlCompareOptions), которые детализируют чувствительность к регистру, акцентам, кане, ширине или всему (двоичному и двоичному2).Конфликт обычно возникает при обращении к параметру SqlString без использования
.Value
или так.ToString()
, что он выполняет неявное преобразование вSqlString
. В этом случае это вызвало бы исключение, говорящее, что идентификаторы LCID не совпадают.Очевидно, есть другие сценарии, такие как выполнение (некоторых / всех?) Сравнений строк, в том числе при использовании Regex, как показывает этот случай (хотя до сих пор я не смог воспроизвести это).
Некоторые идеи для исправлений:
Идеально (ожидания всегда будут оправдываться относительно того, как работают сравнения):
Меньше, чем идеал (поведение языка Windows может не совпадать с правилами равенства и сортировки, и поэтому могут быть неожиданные результаты):
.ToString
метод или.Value
свойство, которые оба возвращают строку без LCID SQL Server, поэтому все операции будут использовать ОС LCID.Может помочь:
SqlChars
вместо,SqlString
поскольку он не приносит LCID и информацию о сопоставлении с SQL ServerStringComparison.InvariantCulture
:String.Compare(string, string, StringComparison.InvariantCulture)
илиString.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
RegexOptions.CultureInvariant
источник
Обновленный ..
Локализация отличается между SQL Engine и оконным сервером, как указывает @srutzky:
Следующее изменение в коде - установка опции
RegexOptions.CultureInvariant
обходит ошибку. Неизмененный код не приведет к сбою SQL Server 2012 в Windows Server 2012R2 с теми же настройками языка, но и в SQL Server 2014.источник
SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;
. Вполне возможно, что проблема была в конфликте языковых настроек. Ваше решение все еще может быть лучшим путем, но обычно не нужно использоватьToString()
вместоValue
свойства наSqlString
s. Так что было бы просто неплохо подтвердить ситуацию.RegexOptions.CultureInvariant
так как вы не передаетеOptions
переменную вRegex.IsMatch(sqldata, regex)
. То, что изменилось между вашим исходным кодом и новым рабочим кодом, это то, что вы перешли от использованияSqlString.Value
кSqlString.ToString()
. Я подозреваю, что вы бы увидели такое же фиксированное поведение, если бы вы переключились на использованиеSqlChars
. Но я бы просто сделал это в качестве теста. Наилучшим подходом является изменение LCID Windows или SQL Server для соответствия другому. Вы также можете удалить статическую переменную Options..Value
свойством aSqlString
as, по-видимому, возвращает то же внутреннее значение, что и.ToString()
метод. Я все еще расследую и обновлю свой ответ всем, что найду :).RegexOptions.IgnoreCase
а другой нет. Я создал подобную среду: Windows (8.0) с помощью LCID 1033, SQL Server DB имеет LCID из 1039, используя один и тот же RegEx , что вы в курсе, делатьCOUNT(*)
наVARCHAR
поле , заполненном GUIDs, используя образец'[0-3â].*'
, на столе с 10 миллионами строк. Это SQL Server 2012, а не 2014, хотя я не думаю, что это должно иметь значение.