Eu tenho esse pequeno CLR que faz uma função RegEX em uma seqüência de caracteres em colunas.
Ao executar no SQL Server 2014 (12.0.2000) no Windows Server 2012R2, o processo falha com
Msg 0, Nível 11, Estado 0, Linha 0 Ocorreu um erro grave no comando atual. Os resultados, se existirem, deveriam ser descartados.
e dá um despejo de pilha se eu fizer
select count (*) from table where (CLRREGEX,'Regex')
mas quando eu faço
select * from table where (CLRREGEX,'Regex')
retorna as linhas.
Funciona perfeitamente na mesma versão do SQL Server em execução no Windows 8.1.
Alguma ideia?
- Edit É o mais simples possível
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);
}
}
Então, com pequenas mudanças, isso funciona agora: A principal lição em C # parece ser a mesma que em TSQL, cuidado com a conversão implícita de dados.
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
fonte
fonte
SqlFunction
método está marcado comoIsDeterministic=true
? A montagem está marcada comoSAFE
?[SqlFunction]
atributo duplicado . Esse é o código exato? Eu não acho que isso seria compilado. A distinção do Framework versão 2.0 / 3.0 / 3.5 não é um problema, pois você está usando 4.0 / 4.5 / 4.5.x / etc ou o que estiver nesse servidor, pois você está no SQL Server 2014, que está vinculado à versão 4. do CLR. servidor mostrando o problema de 32 bits? Quanta memória ela tem em comparação com os outros servidores? E você verificou os logs do SQL Server logo após receber esse erro?MatchTimeout
propriedade. Mas também não acho que esse seja o problema se você está passando apenas 5 caracteres no máximo. Ele é possível que esta máquina tem um corrompido instalação do .NET Framework, e que pode ser reparado, uma vez truta actividades de pesca deixaram ;-). Além disso,[0-9].*
é simples, mas também ineficiente, pois corresponde a todos os caracteres, se houver, após o primeiro dígito; usando apenas[0-9]
para umIsMatch
é melhor.DataAccessKind
paraRead
? Isso diminui a velocidade e você não está acessando dados. Além disso, percebo que parece estar funcionando agora, mas eu seria cauteloso ao usar oToString()
método em oposição àValue
propriedade, pois não acho que o ToString lida com codificações corretamente ou algo assim. Como está definido o agrupamento de bancos de dados? Obviamente, acabei de reler um dos seus comentários acima e vejo que a coluna é VARCHAR em vez de NVARCHAR. Esse campo tem um agrupamento diferente do banco de dados?Respostas:
O problema é um conflito de localidade entre o sistema operacional Windows e o SQL Server (especificamente o banco de dados em que o assembly está carregado). Você pode executar a seguinte consulta para ver o que eles estão definidos para:
Se eles são diferentes, você pode definitivamente ter algum comportamento "estranho", como o que está vendo. A questão é que:
SqlString
inclui mais do que apenas o próprio texto: inclui o agrupamento padrão do banco de dados no qual o assembly existe. O agrupamento é composto por duas informações: as informações da localidade (por exemplo, LCID) e as opções de comparação (por exemplo, SqlCompareOptions) que detalham a sensibilidade a maiúsculas, minúsculas, kana, largura ou tudo o mais (binário e binário2).O conflito geralmente ocorre ao fazer referência a um parâmetro SqlString sem usar
.Value
ou para.ToString()
que ele faça uma conversão implícitaSqlString
. Nesse caso, causaria uma exceção dizendo que os LCIDs não correspondem.Aparentemente, existem outros cenários, como a comparação (algumas / todas?) De cadeias, inclusive ao usar o Regex, como mostra este caso (embora até agora eu não tenha sido capaz de reproduzir isso).
Algumas idéias para correções:
Ideal (sempre serão atendidas as expectativas em relação ao funcionamento das comparações):
Inferior ao ideal (o comportamento da localidade do Windows pode não ser as mesmas regras para igualdade e classificação e, portanto, poderia haver resultados inesperados):
.ToString
método ou a.Value
propriedade, que retornará a sequência sem o LCID do SQL Server, para que todas as operações utilizem o LCID do sistema operacional.Pode ajudar:
SqlChars
vez deSqlString
não trazer as informações de LCID e agrupamento do SQL ServerStringComparison.InvariantCulture
:String.Compare(string, string, StringComparison.InvariantCulture)
ouString.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
RegexOptions.CultureInvariant
fonte
Atualizada..
A localização é diferente entre o Mecanismo SQL e a janela Servidor, como @srutzky aponta:
A seguinte alteração no código - definir a opção
RegexOptions.CultureInvariant
contorna o erro. O código inalterado não trava o SQL Server 2012 no Windows Server 2012R2 com as mesmas configurações de idioma, mas o faz no SQL Server 2014.fonte
SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;
. É bem possível que o problema tenha sido um conflito nas configurações de idioma. Sua solução ainda pode ser o melhor caminho a percorrer, mas geralmente não deve ser necessário usar emToString()
vez daValue
propriedadeSqlString
s. Seria bom confirmar a situação.RegexOptions.CultureInvariant
pois você não passa aOptions
variávelRegex.IsMatch(sqldata, regex)
. O que mudou entre o código original e o novo código funcional é que você passou deSqlString.Value
paraSqlString.ToString()
. Eu suspeito que você veria o mesmo comportamento fixo se você passasse a usarSqlChars
. Mas eu faria isso como um teste. A melhor abordagem é alterar o LCID do Windows ou do SQL Server para corresponder ao outro. Você também pode remover a variável estática Options..Value
propriedade de aSqlString
que aparentemente retorna o mesmo valor interno que o.ToString()
método. Ainda estou investigando e atualizarei minha resposta com o que encontrar :).RegexOptions.IgnoreCase
enquanto o outro não. Eu configurei um ambiente semelhante: Windows (8.0) usando LCID de 1033, o SQL Server DB tem LCID de 1039, usando o mesmo RegEx que você postou, fazendo umCOUNT(*)
em umVARCHAR
campo cheio de GUIDs, usando um padrão de'[0-3â].*'
, em uma tabela com 10 milhões de linhas. É o SQL Server 2012, não 2014, embora eu não ache que isso deva importar.