T-SQL IsNumeric () и Linq-to-SQL - PullRequest
       56

T-SQL IsNumeric () и Linq-to-SQL

7 голосов
/ 25 февраля 2010

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

EU999999 («9» - любая цифра)

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

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

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

Какое будет лучшее решение?

Ответы [ 4 ]

12 голосов
/ 25 февраля 2010

Хотя 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;
11 голосов
/ 25 февраля 2010

Вы можете использовать функцию 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 и добавить к ней текст "EU", чтобы избежать получения второго запросаимя столбца.

РЕДАКТИРОВАТЬ: , как упомянуто в комментариях, недостатком этого подхода является то, что упорядочение выполняется по тексту, а не по числовым значениям, и в настоящее время нет перевода для Int32.ParseSQL.

1 голос
/ 25 февраля 2010

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

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

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 голосов
/ 10 июня 2010

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

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

...