2010-02-24 27 views
6

我需要找到滿足一定的格式約定數據庫中的最高值。具體來說,我想找個看起來像T-SQL則IsNumeric()和LINQ到SQL

EU999999(「9」是任何數字)

選擇MAX(COL)將返回類似的最高值「EUZ ......」爲我想排除的實例。 下面的查詢做的伎倆,但我無法通過LINQ到SQL產生這種。 SQL Server中的isnumeric()函數似乎沒有翻譯。

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

可變長度表達式的一種非常聰明的替代方法。在字符串中的任何位置查找非數字字符。謝謝你的提示! – BlueMonkMN

1

正如你所說的,沒有翻譯則IsNumeric從LINQ to SQL的。有幾個選項,你已經編寫了數據庫函數和存儲過程。我想再添加兩個。

選項1:您可以通過混合使用LINQ LINQ to SQL的對象來做到這一點,但是當你有一個大的數據庫,不要指望偉大的表現:

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

您可以通過添加一個方法爲DataContext的一個局部類利用ISNUMERIC功能。這與使用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.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的存在首先是存在的。