2010-02-24 6 views
6

Мне нужно найти максимальное значение из базы данных, которая удовлетворяет определенному соглашению о форматировании. В частности, я хотел бы найти самое высокое значение, которое выглядит какT-SQL IsNumeric() и Linq-to-SQL

EU999999 («9» быть любая цифра)

выбрать макс (Col) возвращает что-то вроде «EUZ ...» для экземпляр, который я хочу исключить. Следующий запрос выполняет трюк, но я не могу это сделать через Linq-to-SQL. Кажется, что нет перевода для функции isnumeric() в SQL Server.

select max(col) from table where col like 'EU%' 
    and 1=isnumeric(replace(col, 'EU', '')) 

Запись функции базы данных, хранимую процедуру или что-нибудь еще в этом роде далеко вниз список моих предпочтительных решений, так как эта таблица занимает центральное место в мое приложение, и я не могу легко заменить объект таблицы с чем-то еще ,

Какое следующее лучшее решение?

ответ

10

Хотя ISNUMERIC отсутствует, вы всегда можете попробовать почти эквивалент NOT LIKE '%[^0-9]%, т.е. нет нецифровой в строке, или в качестве альтернативы, строка пуста или состоит только из цифр:

from x in table 
where SqlMethods.Like(x.col, 'EU[0-9]%') // starts with EU and at least one digit 
    && !SqlMethods.Like(x.col, '__%[^0-9]%') // and no non-digits 
select x; 

Конечно, если вы знаете, что количество цифр фиксирована, это может быть упрощено до

from x in table 
where SqlMethods.Like(x.col, 'EU[0-9][0-9][0-9][0-9][0-9][0-9]') 
select x; 
+0

Мне нравится ваше предложение, и я его принимаю, но мой префикс (и длина) не является постоянным, к сожалению, поэтому мне пришлось бы генерировать выражение динамически. Мне это не нравится, и я думаю о способах изменения схемы, чтобы я мог обойти это. – cdonner

+0

Одним из преимуществ использования 'LIKE' без подстановочных знаков на передней панели является то, что это очень удобный индекс (может быть проведена проверка диапазона по индексу). – Ruben

+0

Очень умная альтернатива IsNumeric на выражениях переменной длины. Найдите нечисловой символ в любом месте строки. Спасибо за совет! – BlueMonkMN

1

Как вы сказали, нет перевода для IsNumeric из LINQ к SQL. Есть несколько вариантов, вы уже написали функцию базы данных и хранимую процедуру. Мне нравится добавить еще два.

Вариант 1: Вы можете сделать это путем смешивания LINQ для SQL с помощью LINQ к объектам, но когда у вас есть большая база данных, не следует ожидать большой производительности:

var cols = (from c in db.Table where c.StartsWith("EU") select c).ToList(); 
var stripped = from c in cols select int.Parse(c.Replace("EU", "")); 
var max = stripped.Max(); 

Вариант 2: изменить ваш схема базы данных :-)

+0

не вариант - слишком много данных в этой таблице. – cdonner

+0

все еще, +1 за предложение изменить мою схему. – cdonner

10

Вы можете использовать функцию ISNUMERIC, добавив метод для частичного класса для DataContext. Это было бы похоже на использование UDF.

В вашем разделяемом классе DataContext Добавить это:

partial class MyDataContext 
{ 
    [Function(Name = "ISNUMERIC", IsComposable = true)] 
    public int IsNumeric(string input) 
    { 
     throw new NotImplementedException(); // this won't get called 
    } 
} 

Тогда ваш код будет использовать его таким образом:

var query = dc.TableName 
       .Select(p => new { p.Col, ReplacedText = p.Col.Replace("EU", "") }) 
       .Where(p => SqlMethods.Like(p.Col, "EU%") 
         && dc.IsNumeric(p.ReplacedText) == 1) 
       .OrderByDescending(p => p.ReplacedText) 
       .First() 
       .Col; 

Console.WriteLine(query); 

Или вы могли бы использовать MAX:

var query = dc.TableName 
       .Select(p => new { p.Col, ReplacedText = p.Col.Replace("EU", "") }) 
       .Where(p => SqlMethods.Like(p.Col, "EU%") 
        && dc.IsNumeric(p.ReplacedText) == 1); 

var result = query.Where(p => p.ReplacedText == query.Max(p => p.ReplacedText)) 
        .First() 
        .Col; 

Console.WriteLine("Max: {0}, Result: {1}", max, result); 

В зависимости от вашей конечной цели можно было бы остановиться на переменной max и добавить ее с помощью " ЕС ", чтобы избежать второго запроса, который получает имя столбца.

EDIT: как уже упоминалось в комментариях, недостаток этого подхода заключается в том, что упорядочение делается на текст, а не числовые значения, и нет в настоящее время нет перевода для Int32.Parse на SQL.

+0

+1. Я очень удивлен, что это возможно. Не забывайте, что порядок * OrderByDescending (p => p.ReplacedText) * основан на тексте (ReplacedText - строка/varchar), поэтому это может не привести к правильному результату. Перед сортировкой его нужно отличить до целого числа. – Steven

+0

@Steven спасибо, это аккуратное обходное решение для простых функций. Вы правы в заказе. К сожалению, для этого нет обходного пути, и 'Int32.Parse' не имеет перевода в SQL, поэтому для числовой сортировки лучше всего подходит UDF/SPROC. –

+0

То же самое здесь, это одна из самых маленьких вещей, которые я видел в то время. – cdonner

0

Мое предложение - вернуться к встроенному SQL и использовать метод DataContext.ExecuteQuery(). Вы бы использовали SQL-запрос, который вы опубликовали в начале.

Это то, что я сделал в подобных ситуациях. Не идеально, предоставляется, из-за отсутствия проверки типов и возможных синтаксических ошибок, но просто убедитесь, что он включен в любые модульные тесты. Не каждый возможный запрос покрывается синтаксисом Linq, следовательно, существует ExecuteQuery в первую очередь.