2010-02-24 42 views
6

Tôi cần tìm giá trị cao nhất từ ​​cơ sở dữ liệu đáp ứng một quy ước định dạng nhất định. Cụ thể, tôi muốn tìm giá trị cao nhất mà trông giống nhưT-SQL IsNumeric() và LINQ-to-SQL

EU999999 ('9' là bất kỳ chữ số)

chọn max (col) sẽ trở lại một cái gì đó như 'EUZ ...' cho ví dụ mà tôi muốn loại trừ. Truy vấn sau thực hiện thủ thuật, nhưng tôi không thể tạo ra điều này thông qua LINQ-to-SQL. Dường như không có bản dịch cho hàm isnumeric() trong SQL Server.

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

Viết một chức năng cơ sở dữ liệu, thủ tục lưu trữ, hoặc bất cứ thứ gì của thiên nhiên mà còn lâu mới xuống danh sách các giải pháp ưa thích của tôi, bởi vì bảng này là trung tâm ứng dụng của tôi và tôi không thể dễ dàng thay thế các đối tượng bảng với cái gì khác .

Giải pháp tốt nhất tiếp theo là gì?

Trả lời

10

Mặc dù ISNUMERIC là mất tích, bạn luôn có thể thử gần tương đương NOT LIKE '%[^0-9]%, tức là, không có phi chữ số trong chuỗi, hoặc cách khác, chuỗi rỗng hoặc chỉ bao gồm các chữ số:

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; 

Tất nhiên, nếu bạn biết rằng số chữ số được cố định, điều này có thể được đơn giản hóa thành

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

Tôi thích đề xuất của bạn và tôi chấp nhận nó, nhưng tiền tố của tôi (và chiều dài) không phải là hằng số, thật không may, vì vậy tôi sẽ phải tạo biểu thức động. Điều đó tôi không thích, và tôi đang nghĩ về cách thay đổi lược đồ để tôi có thể giải quyết vấn đề này. – cdonner

+0

Một trong những ưu điểm của việc sử dụng 'LIKE' không có ký tự đại diện ở mặt trước là điều này rất thân thiện với chỉ mục (có thể kiểm tra phạm vi với chỉ mục). – Ruben

+0

Cách thay thế rất thông minh đối với IsNumeric trên các biểu thức có độ dài thay đổi. Tìm ký tự không phải số ở bất kỳ đâu trong chuỗi. Cảm ơn vì tiền hỗ trợ! – BlueMonkMN

1

Như bạn đã nói, không có bản dịch cho IsNumeric từ LINQ to SQL. Có một vài tùy chọn, bạn đã viết chức năng cơ sở dữ liệu và thủ tục lưu trữ xuống. Tôi muốn thêm hai nữa.

Lựa chọn 1: Bạn có thể làm điều này bằng cách trộn LINQ to SQL với LINQ to Objects, nhưng khi bạn đã có một cơ sở dữ liệu lớn, tôi không mong đợi hiệu suất tuyệt vời:

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(); 

Phương án 2: thay đổi của bạn lược đồ cơ sở dữ liệu :-)

+0

không phải là một tùy chọn - cách quá nhiều dữ liệu trong bảng này. – cdonner

+0

vẫn còn, +1 cho đề xuất thay đổi giản đồ của tôi. – cdonner

10

Bạn có thể sử dụng hàm ISNUMERIC bằng cách thêm một phương thức vào một lớp từng phần cho DataContext. Nó sẽ tương tự như sử dụng một UDF.

Trong partial class DataContext của bạn thêm này:

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

Sau đó, mã của bạn sẽ sử dụng nó theo cách này:

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); 

Hoặc bạn có thể sử dụng 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); 

Tùy thuộc vào mục tiêu cuối cùng của bạn, bạn có thể dừng lại ở biến số max và thêm vào đó với biến " EU "văn bản để tránh truy vấn thứ 2 nhận tên cột.

EDIT: như đã đề cập trong nhận xét, thiếu sót của phương pháp này là đặt hàng được thực hiện trên văn bản thay vì giá trị số và hiện tại không có bản dịch cho Int32.Parse trên SQL.

+0

+1. Tôi khá ngạc nhiên vì điều này là có thể. Đừng quên thứ tự * OrderByDescending (p => p.ReplacedText) * là văn bản dựa (ReplacedText là một chuỗi/varchar), do đó, điều này có thể không mang lại kết quả thích hợp. Nó phải được đúc thành một số nguyên trước khi sắp xếp. – Steven

+0

@Steven cảm ơn, đó là giải pháp gọn gàng cho các chức năng đơn giản. Bạn nói đúng về thứ tự. Thật không may là không có cách giải quyết nào cho điều đó và 'Int32.Parse' không dịch sang SQL để phân loại số một UDF/SPROC thực sự là tốt nhất. –

+0

Tương tự ở đây, đây là một trong những điều mượt mà tôi đã thấy trong một thời gian. – cdonner

0

Đề xuất của tôi là quay trở lại SQL nội tuyến và sử dụng phương thức DataContext.ExecuteQuery(). Bạn sẽ sử dụng truy vấn SQL bạn đã đăng ngay từ đầu.

Đây là những gì tôi đã làm trong các tình huống tương tự. Không lý tưởng, được cấp, do thiếu kiểm tra loại và các lỗi cú pháp có thể, nhưng chỉ cần đảm bảo rằng nó được bao gồm trong bất kỳ bài kiểm tra đơn vị nào. Không phải mọi truy vấn có thể được bao hàm bởi cú pháp LINQ, do đó sự tồn tại của ExecuteQuery ở vị trí đầu tiên.

Các vấn đề liên quan