簡體   English   中英

如何在 LINQ-to-SQL 中模擬正則表達式

[英]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(...) 做到這一點?

更新 2:

這是一個運行速度非常慢的查詢。 我有 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM