[英]How to simulate regular expressions in LINQ-to-SQL
我有一個包含客戶帳號的數據庫表。 在同一個表中是與生產格式不匹配的測試帳戶:例如,“A1111”是生產但“JTest”不是。 我有只提取我的生產帳戶的正則表達式。 我需要一個特定的編譯查詢來僅提取生產帳戶。 該查詢按地區和日期為我提供了客戶計數; 每個區域內的概念計數:
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()
)));
問題是我在運行時收到以下消息:
方法“Boolean IsMatch(System.String, System.String)”不支持轉換為 SQL。
我在看一個用戶定義的函數:
static Func<striing, bool> IsProduction = (AcctNum) => Regex.IsMatch(AcctNum, ProductionMask);
這也行不通。 我不想迭代檢索到的記錄以進一步過濾,除非沒有其他方法可以做到這一點。
有沒有辦法用 Predicate Builder 做到這一點?
我認為另一種選擇是使用:
where SqlMethods.Like (cust.AcctNum, ProductionMask)
但是,我的 ProductionMask 是為 Regex 編寫的:
^[B,G]\d{4}$
有沒有辦法用 SqlMethods.Like(...) 做到這一點?
這是一個運行速度非常慢的查詢。 我有 3 個區域運行此查詢,記錄計數和返回時間為:
263:903毫秒
342: 822 毫秒
146: 711 毫秒
我更改了查詢以使用以下內容代替Regex.IsMatch
:
where SqlMethods.Like(cust.Acct, ProductionMask)
其中ProductionMask = "[bBgG][0-9][0-9][0-9][0-9]"
等效的正則表達式是: ^[B,G]\\d{4}$
如果有人看到這兩個面具不應該產生相同的結果,請告訴我...
特別感謝 Roman Khramtsov 和 db_developer 提供參考信息,並感謝 Microsoft :P
Sql Server 的 RegExpLike 擴展
參考鏈接:
http://www.codeproject.com/Articles/42764/Regular-Expressions-in-MS-SQL-Server-2005-2008
http://msdn.microsoft.com/en-us/library/dd456847.aspx
Step1:編譯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);
}
}
步驟 2:在數據庫上運行 DbInstall.sql SQL
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
第三步:在模型圖上右擊,選擇“Update Model from Database...”,使用更新向導將存儲的函數添加到模型中。
第 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 Entities 上使用正則表達式 :)
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();
您在使用 LINQ-to-SQL 嗎? 如果是這樣, MSDN 論壇聲明如下:
LINQ to SQL 無法將正則表達式轉換為 SQL,因為 SQL 端不支持 Regex。
不過,它確實提供了 3 種選擇。
我遇到了同樣的問題,但設法擺脫了它。 我知道它很慢但有效,任何優化/錯誤修復提示都會受到歡迎:) 代碼首先收集數據然后處理,所以你需要在調用toarray()
或購買更多內存之前盡可能多地過濾:)
希望它有幫助,享受
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);
}
PS 這是處理輕數據表的一種快速方法,您可以通過獲取所需的列進行處理並返回 ID 列以完全訪問行來改進它。 您可以將我的上一篇文章用於更一般的重型場景。 選擇權在你。
你能把 Regex.IsMatch 替換成
where cust.AcctNum.StartsWith(ProductionMask)
或根據您的需要包含/結束
在 EF 6.2 到 6.4.4 中使用 DbFunctions.Like
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.