C# 如何在LINQ to SQL中模拟正则表达式
我有一个带有客户帐号的数据库表。同一个表中有与生产格式不匹配的测试帐户:例如,“A1111”是生产,但“JTest”不是。我的正则表达式只会提取我的生产帐户。我需要一个特定的编译查询来只提取生产帐户。该查询按地区和日期为我提供客户计数;每个区域内的概念计数:C# 如何在LINQ to SQL中模拟正则表达式,c#,regex,linq-to-sql,C#,Regex,Linq To Sql,我有一个带有客户帐号的数据库表。同一个表中有与生产格式不匹配的测试帐户:例如,“A1111”是生产,但“JTest”不是。我的正则表达式只会提取我的生产帐户。我需要一个特定的编译查询来只提取生产帐户。该查询按地区和日期为我提供客户计数;每个区域内的概念计数: getCustomerDistribution = CompiledQuery.Compile<DataContext, String, DateTime, IEnumerable<ServerLoad>&
getCustomerDistribution = CompiledQuery.Compile<DataContext, String, DateTime, IEnumerable<ServerLoad>>(
(context, region, processDate) => (from cust in context.GetTable<tbl_CustomerDistro>()
where cust.ProcessedDate.Date == processDate.Date
where cust.Region == region
where Regex.IsMatch(cust.AcctNum, ProductionMask)
group cust by new
{
cust.Region,
cust.Concept
} into custDistro
orderby custDistro.Key.Region
select new CustomerDistro
(
custDistro.Key.Region,
custDistro.Key.Concept,
custDistro
.Where(c => c.Concept == custDistro.Key.Concept)
.Select(c => c.Concept).Count()
)));
有没有办法用SqlMethods实现这一点,比如(…)
更新2:
这是一个运行速度非常慢的查询。我有3个区域运行此查询,记录计数和返回时间为:263:903ms
342:822ms
146:711ms您正在使用LINQ到SQL吗?如果是,请说明以下内容: LINQ到SQL无法转换为正则表达式 表达式转换为SQL,因为没有 在SQL端支持正则表达式
但它确实给出了3种选择。您能用
where cust.AcctNum.StartsWith(ProductionMask)
或者包含/EndsWith,具体取决于您的需要我将查询更改为使用以下内容来代替
Regex.IsMatch
:
where SqlMethods.Like(cust.Acct, ProductionMask)
其中ProductionMask=“[bBgG][0-9][0-9][0-9][0-9]”
等价的正则表达式是:^[B,G]\d{4}$
如果有人看到这两个面具不应该产生相同的结果,请告诉我…我也有同样的问题,但还是设法解决了。 我知道这很慢,但很有效,欢迎任何优化/错误修复提示:) 代码首先收集数据,然后进行处理, 因此,在调用
toarray()
或购买更多ram:)之前,您需要尽可能多地进行过滤希望有帮助, 享受 另外,这是一种处理灯光数据表的快速方法,您可以 通过获取所需的列进行处理和 返回对行的完全访问的ID列。你可以用我最后的帖子
对于更一般的重载场景。您可以选择。特别感谢Roman Khramtsov和db_开发者提供参考信息, 感谢微软:P Sql Server的RegExpLike扩展 参考链接:
步骤1:编译SqlRegularExpressions.cs以生成SqlRegularExpressions.dll
// SqlRegularExpressions.cs
// © Copyright 2009, Roman Khramtsov / Major League - SqlRegularExpressions
using System;
using System.Data.SqlTypes; //SqlChars
using System.Collections; //IEnumerable
using System.Text.RegularExpressions; //Match, Regex
using Microsoft.SqlServer.Server; //SqlFunctionAttribute
/// <summary>
/// Class that allows to support regular expressions in MS SQL Server 2005/2008
/// </summary>
public partial class SqlRegularExpressions
{
/// <summary>
/// Checks string on match to regular expression
/// </summary>
/// <param name="text">string to check</param>
/// <param name="pattern">regular expression</param>
/// <returns>true - text consists match one at least, false - no matches</returns>
[SqlFunction]
public static bool Like(string text, string pattern, int options)
{
return (Regex.IsMatch(text, pattern, (RegexOptions)options));
}
/// <summary>
/// Gets matches from text using pattern
/// </summary>
/// <param name="text">text to parse</param>
/// <param name="pattern">regular expression pattern</param>
/// <returns>MatchCollection</returns>
[SqlFunction(FillRowMethodName = "FillMatch")]
public static IEnumerable GetMatches(string text, string pattern, int options)
{
return Regex.Matches(text, pattern, (RegexOptions)options);
}
/// <summary>
/// Parses match-object and returns its parameters
/// </summary>
/// <param name="obj">Match-object</param>
/// <param name="index">TThe zero-based starting position in the original string where the captured
/// substring was found</param>
/// <param name="length">The length of the captured substring.</param>
/// <param name="value">The actual substring that was captured by the match.</param>
public static void FillMatch(object obj, out int index, out int length, out SqlChars value)
{
Match match = (Match)obj;
index = match.Index;
length = match.Length;
value = new SqlChars(match.Value);
}
}
DbUninstall.sql
步骤3:在模型图上单击鼠标右键,选择“从数据库更新模型…”,使用更新向导将存储的函数添加到模型中。步骤4:在实体上下文类中创建导入的函数
public class TheCompanyContext : Entities
{
// Please check your entity store name
[EdmFunction("TheCompanyDbModel.Store", "RegExpLike")]
public bool RegExpLike(string text, string pattern, int options)
{
throw new NotSupportedException("Direct calls are not supported.");
}
}
第5步:最后,您可以在LINQ to实体上使用正则表达式:)
在EF 6.2到6.4.4中,使用DbFunctions。例如我认为Contains/EndsWith/etc.有点有限。请参阅上面我的更新。我正在考虑Joe提到的另一种方法,即使用另一种方法重新处理查询…因此我正在研究SqlMethods.Like()。我想知道选项2是否正确。“使用CLR与SQL 2005集成来编写自定义正则表达式函数”可以与LINQ to SQL或EF结合使用?@Grastveit-我不明白为什么不使用EF,因为您可以在EF中调用一个过程,所以如果它只是一个CLR过程,那就可以了。至于LINQ到SQL,我不太确定。几年前,微软非常反对EF,这是有原因的——它的局限性。@IAbstract——您的问题确实提出了按名称格式将生产记录与非生产帐户分开的方案,因此
如或BeginsWith
应该适合您。如果您有一个更复杂的正则表达式,那么可能没有。我正在考虑将类似于Customers.Where(c=>MyClrClass.MyRegexMatch(c.AcctNum,ProductionMask))
的内容转换为调用Where子句中clr函数的sql查询。这就是你所看到的吗?我想你的意思是[bBgG,][0-9][0-9][0-9][0-9]
;[B,G]
中的逗号除了“匹配一个逗号”之外没有其他含义。除非原始正则表达式中的逗号是个错误(我想是的,但仍然是)。@Ruben:原始正则表达式很久以前就提供了:)可能不需要“,”…不过,谢谢你提供的信息!这样做的(大规模)问题是,您检索整个数据库,然后在内存中进行过滤,这显然不是非常可伸缩的。因此,随着数据库的增长,它变得越来越慢。这并不是对原始问题的回答,因为它与修改SQL无关。是的,你是对的,我的代码不适用于具有数十万行的大型表。“SqlMethods.Like”对我来说不起作用,可能是因为我使用的是实体框架。也许使用BeginsWith、EndsWith和Contains对某人来说是最好的选择,或者我认为最好的选择是从存储函数中选择一个regexp参数并返回表行。但最好的是等待在LINQ中完全支持Microsoft实体的正则表达式,我再也等不及了:)
Regex rx = LikeToRegEx(emailToMatch);
User[] qry = (from u in context.Users
where u.ApplicationName == pApplicationName
orderby u.Username
select u)
.ToArray()
.Where(u => rx.IsMatch(u.Email))
.ToArray();
// -- LikeToRegEx : Converts SQL like match pattern to a regular expression --
public Regex LikeToRegEx(string likestr, RegexOptions opt = RegexOptions.None)
{
likestr = likestr
.Replace("*", ".")
.Replace("+", ".")
.Replace("(", ".")
.Replace("[", ".")
.Replace("/", ".")
.Replace("\\", ".")
.Replace("^", ".")
.Replace("$", ".")
.Replace("_", ".")
.Replace("%", ".*");
return new Regex(likestr, opt);
}
// SqlRegularExpressions.cs
// © Copyright 2009, Roman Khramtsov / Major League - SqlRegularExpressions
using System;
using System.Data.SqlTypes; //SqlChars
using System.Collections; //IEnumerable
using System.Text.RegularExpressions; //Match, Regex
using Microsoft.SqlServer.Server; //SqlFunctionAttribute
/// <summary>
/// Class that allows to support regular expressions in MS SQL Server 2005/2008
/// </summary>
public partial class SqlRegularExpressions
{
/// <summary>
/// Checks string on match to regular expression
/// </summary>
/// <param name="text">string to check</param>
/// <param name="pattern">regular expression</param>
/// <returns>true - text consists match one at least, false - no matches</returns>
[SqlFunction]
public static bool Like(string text, string pattern, int options)
{
return (Regex.IsMatch(text, pattern, (RegexOptions)options));
}
/// <summary>
/// Gets matches from text using pattern
/// </summary>
/// <param name="text">text to parse</param>
/// <param name="pattern">regular expression pattern</param>
/// <returns>MatchCollection</returns>
[SqlFunction(FillRowMethodName = "FillMatch")]
public static IEnumerable GetMatches(string text, string pattern, int options)
{
return Regex.Matches(text, pattern, (RegexOptions)options);
}
/// <summary>
/// Parses match-object and returns its parameters
/// </summary>
/// <param name="obj">Match-object</param>
/// <param name="index">TThe zero-based starting position in the original string where the captured
/// substring was found</param>
/// <param name="length">The length of the captured substring.</param>
/// <param name="value">The actual substring that was captured by the match.</param>
public static void FillMatch(object obj, out int index, out int length, out SqlChars value)
{
Match match = (Match)obj;
index = match.Index;
length = match.Length;
value = new SqlChars(match.Value);
}
}
sp_configure 'clr enabled', 1
reconfigure
go
--needs full path to DLL
create assembly SqlRegularExpressions
from '..\SqlRegularExpressions.dll'
with PERMISSION_SET = SAFE
go
create function RegExpLike(@Text nvarchar(max), @Pattern nvarchar(255), @Options int = 0)
returns bit
as external name SqlRegularExpressions.SqlRegularExpressions.[Like]
go
create function RegExpMatches(@text nvarchar(max), @pattern nvarchar(255), @Options int = 0)
returns table ([Index] int, [Length] int, [Value] nvarchar(255))
as external name SqlRegularExpressions.SqlRegularExpressions.GetMatches
go
drop function RegExpLike
drop function RegExpMatches
drop assembly SqlRegularExpressions
go
sp_configure 'clr enabled', 0
reconfigure
go
public class TheCompanyContext : Entities
{
// Please check your entity store name
[EdmFunction("TheCompanyDbModel.Store", "RegExpLike")]
public bool RegExpLike(string text, string pattern, int options)
{
throw new NotSupportedException("Direct calls are not supported.");
}
}
User[] qry = (from u in context.Users
where u.ApplicationName == pApplicationName
&& context.RegExpLike(u.Username, usernameToMatch, (int)RegexOptions.IgnoreCase)
orderby u.Username
select u)
.Skip(startIndex)
.Take(pageSize)
.ToArray();