Подтвердить что ты не робот

Как имитировать регулярные выражения в LINQ-to-SQL

У меня есть таблица базы данных с номерами учетных записей. Внутри той же таблицы находятся тестовые учетные записи, которые не соответствуют форматированию продукции: например, "A1111" - это производство, но "JTest" - нет. У меня есть Regex, который вытащит только мои производственные счета. Мне нужен конкретный скомпилированный запрос, чтобы вытащить только производственные учетные записи. Запрос дает мне количество клиентов по регионам и дате; и подсчет понятий в каждом регионе:

        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: 903ms
  342: 822ms
  146: 711 мс

4b9b3361

Ответ 1

Я изменил запрос, используя вместо Regex.IsMatch следующее:

where SqlMethods.Like(cust.Acct, ProductionMask)  

где ProductionMask = "[bBgG][0-9][0-9][0-9][0-9]"

эквивалентный RegEx: ^[B,G]\d{4}$

Если кто-нибудь увидит, что 2 маски не должны давать одинаковые результаты, сообщите мне...

Ответ 2

Используете ли вы LINQ-to-SQL? Если да, форумы MSDN сообщают следующее:

LINQ to SQL не может переводить регулярные выражения для SQL, потому что нет поддержка Regex на конце SQL.

Однако он дает 3 варианта.

Ответ 3

Особая благодарность Роману Храмцову и db_developer за справочную информацию, и благодаря Microsoft: P

Расширение RegExpLike для сервера Sql

Ссылки:
http://www.codeproject.com/Articles/42764/Regular-Expressions-in-MS-SQL-Server-2005-2008
http://msdn.microsoft.com/en-us/library/dd456847.aspx

Шаг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);
    }

}

Шаг 2: Запустите DbInstall.sql SQL в базе данных

DbInstall.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

DbUninstall.sql

drop function RegExpLike
drop function RegExpMatches

drop assembly SqlRegularExpressions
go

sp_configure 'clr enabled', 0
reconfigure
go

Шаг 3: В правой части диаграммы модели выберите "Обновить модель из базы данных...", используйте мастер обновления, чтобы добавить хранимые функции в модель.
Model diagram context menuUpdate wizardModel browser

Шаг 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();

Ответ 4

Не могли бы вы заменить Regex.IsMatch на

where cust.AcctNum.StartsWith(ProductionMask)

Или Содержит/Концы В зависимости от ваших потребностей

Ответ 5

У меня была такая же проблема, но мне удалось избавиться от нее. Я знаю, что это медленно, но работает, любая подсказка оптимизации/исправления будет приветствоваться:) Сначала код собирает данные, а затем обрабатывает, поэтому вам нужно отфильтровать столько, сколько вы можете, прежде чем звонить 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);
 }

P.S. Это быстрый способ обработки световых таблиц данных, вы можете улучшите его, просто извлекая необходимые столбцы для обработки и просто возвращать идентификационные столбцы для полного доступа к строкам. Вы можете использовать мой последний пост для более общих сценариев тяжелого режима. Выбор за вами.