J'ai ce petit CLR qui fait une fonction RegEX sur une chaîne de colonnes.
Lors de l'exécution sur SQL Server 2014 (12.0.2000) sur Windows Server 2012R2, le processus se bloque avec
Msg 0, niveau 11, état 0, ligne 0 Une erreur grave s'est produite sur la commande actuelle. Le cas échéant, les résultats doivent être ignorés.
et donne un vidage de la pile si je fais
select count (*) from table where (CLRREGEX,'Regex')
mais quand je fais
select * from table where (CLRREGEX,'Regex')
il renvoie les lignes.
Fonctionne parfaitement sur la même version de SQL Server exécutée sur Windows 8.1.
Des idées?
- Modifier C'est aussi simple que possible
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);
}
}
Donc, par petits changements, cela fonctionne maintenant: la leçon principale en C # semble être la même que dans TSQL, méfiez-vous de la conversion implicite des données.
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);
}
SqlFunction
méthode est-elle marquée commeIsDeterministic=true
? L'assemblage est-il marqué commeSAFE
?[SqlFunction]
attribut en double . Est-ce le code exact? Je ne pense pas que cela se compilerait. La distinction Framework version 2.0 / 3.0 / 3.5 n'est pas un problème car vous utilisez 4.0 / 4.5 / 4.5.x / etc ou tout ce qui se trouve sur ce serveur puisque vous êtes sur SQL Server 2014 qui est lié à CLR version 4. Est-ce que serveur montrant le problème 32 bits? Combien de mémoire possède-t-il par rapport aux autres serveurs? Et avez-vous vérifié les journaux SQL Server juste après avoir obtenu cette erreur?MatchTimeout
propriété. Mais je ne pense pas que ce soit vraiment le problème non plus si vous ne passez que 5 caractères max. Il est possible que cette machine ait une installation corrompue du .NET Framework, et qu'elle puisse être réparée une fois que les activités de pêche à la truite ont cessé ;-). En outre,[0-9].*
est simple mais aussi inefficace car il correspond à tous les caractères, le cas échéant, après le premier chiffre; utiliser juste[0-9]
pour unIsMatch
est mieux.DataAccessKind
pourRead
? Cela ralentit simplement et vous ne faites aucun accès aux données. De plus, je me rends compte que cela semble fonctionner maintenant, mais je serais prudent avec l'utilisation de laToString()
méthode par opposition à laValue
propriété car je ne pense pas que ToString gère correctement les encodages, ou quelque chose comme ça. À quoi sert le classement de vos bases de données? Bien sûr, je viens de relire l'un de vos commentaires ci-dessus et de voir que la colonne est VARCHAR au lieu de NVARCHAR. Ce champ a-t-il un classement différent de celui de la base de données?Réponses:
Le problème est un conflit de paramètres régionaux entre le système d'exploitation Windows et SQL Server (en particulier la base de données où l'assembly est chargé). Vous pouvez exécuter la requête suivante pour voir à quoi ils sont tous deux définis:
S'ils sont différents, vous pouvez certainement obtenir un comportement "étrange", comme ce que vous voyez. Le problème est que:
SqlString
inclut plus que le texte lui-même: il inclut le classement par défaut de la base de données dans laquelle l'assembly existe. Le classement est composé de deux informations: les informations locales (c.-à-d. LCID) et les options de comparaison (c.-à-d. SqlCompareOptions) qui détaillent la sensibilité à la casse, aux accents, au kana, à la largeur ou à tout (binaire et binaire2).Le conflit se produit généralement lors du référencement d'un paramètre SqlString sans utiliser
.Value
ou.ToString()
tel qu'il effectue une conversion implicite versSqlString
. Dans ce cas, cela provoquerait une exception indiquant que les LCID ne correspondent pas.Il existe apparemment d'autres scénarios, tels que des comparaisons de chaînes (certaines / toutes?), Y compris lors de l'utilisation de Regex comme le montre ce cas (bien que jusqu'à présent je n'ai pas été en mesure de reproduire cela).
Quelques idées de correctifs:
Idéal (les attentes seront toujours satisfaites quant au fonctionnement des comparaisons):
Moins qu'idéal (le comportement des paramètres régionaux Windows peut ne pas être les mêmes règles d'égalité et de tri et il peut donc y avoir des résultats inattendus):
.ToString
méthode ou la.Value
propriété, qui renvoient toutes les deux la chaîne sans le LCID SQL Server afin que les opérations utilisent toutes le LCID du système d'exploitation.Pourrait aider:
SqlChars
au lieu deSqlString
car il n'apporte pas le LCID et les informations de classement de SQL ServerStringComparison.InvariantCulture
:String.Compare(string, string, StringComparison.InvariantCulture)
ouString.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
RegexOptions.CultureInvariant
la source
Mis à jour..
La localisation est différente entre le moteur SQL et le serveur de fenêtres comme le souligne @srutzky:
La modification suivante du code - la définition de l'option
RegexOptions.CultureInvariant
contourne l'erreur. Le code inchangé ne plantera pas SQL Server 2012 sur Windows Server 2012R2 avec les mêmes paramètres de langue mais le fait sur SQL Server 2014.la source
SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;
. Il est fort possible que le problème soit un conflit dans les paramètres de langue. Votre solution est peut-être encore la meilleure solution, mais en général, il ne devrait pas être nécessaire d'utiliser à laToString()
place de laValue
propriété du parSqlString
. Ce serait donc bien de confirmer la situation.RegexOptions.CultureInvariant
puisque vous ne transmettez pas laOptions
variable àRegex.IsMatch(sqldata, regex)
. La chose qui a changé entre votre code d'origine et le nouveau code de travail est que vous êtes passé deSqlString.Value
àSqlString.ToString()
. Je soupçonne que vous verriez le même comportement fixe si vous passiez à l'utilisationSqlChars
. Mais je ferais juste cela comme un test. La meilleure approche consiste à modifier le LCID de Windows ou SQL Server pour correspondre à l'autre. Vous pouvez également supprimer la variable statique Options..Value
propriété d'unSqlString
as qui renvoie apparemment la même valeur interne que la.ToString()
méthode. J'étudie toujours et mettrai à jour ma réponse avec tout ce que je trouverai :).RegexOptions.IgnoreCase
tandis que l'autre ne le fait pas. J'ai mis en place un environnement similaire: Windows (8.0) en utilisant LCID de 1033, SQL Server DB a LCID de 1039, en utilisant le même RegEx que vous avez publié, en faisant unCOUNT(*)
sur unVARCHAR
champ rempli de GUID, en utilisant un modèle de'[0-3â].*'
, sur une table avec 10 millions de lignes. C'est SQL Server 2012, pas 2014, bien que je ne pense pas que cela devrait avoir d'importance.