2010-12-13 59 views
22

Trong SQL bạn có thể thể hiện nhiều uẩn trong một truy vấn cơ sở dữ liệu đơn như thế này:Nhiều chức năng SQL tổng hợp trong một đơn LINQ-to-Đối tượng truy vấn

SELECT MIN(p.x), MAX(p.x), MIN(p.y), MAX(p.y) 
FROM Places p JOIN Logs l on p.Id = l.PlaceId 
WHERE l.OwnerId = @OwnerId 

Có thể làm một điều tương đương sử dụng LINQ-to -Entities? Tôi tìm thấy một similar question cho thấy nó không thể cho Linq-to-SQL nhưng tôi muốn nghĩ rằng tôi sẽ không phải làm điều trên bằng cách sử dụng bốn chuyến đi vòng đến DB.

+0

+1 cho câu hỏi thú vị đã không tự mình nhìn vào nó nhưng sẽ thật kỳ lạ nếu nó không hoạt động. Bạn không thể sử dụng cú pháp 'let'? –

+0

@Thomas, như xa như tôi có thể thấy nó không thể gán một tập hợp bằng cách sử dụng 'let', mà là một giá trị duy nhất cho một mục trong tập hợp. Tôi đã sử dụng EF trong vài tháng và đã rất ngạc nhiên khi đạt được rào cản này ngày hôm nay. Tôi thực sự không nghĩ rằng nó có thể! Câu trả lời đã xóa của 01 thợ săn @ –

+0

@ hunter. Bạn đã thử à? Ngoài ra, 'let' thực sự làm việc với các bộ. Bạn đã thử chưa Tôi có mã sản xuất làm cả hai. –

Trả lời

24

Giả sử bạn có các câu lệnh SQL sau:

select sum(A), max(B), avg(C) from TBL group by D 

Hãy thử điều này trong C# :

from t in table 
    group t by D 
    into g 
    select new { 
    s = g.Sum(x => x.A), 
    m = g.Max(x => x.B), 
    a = g.Average(x => x.C) 
    } 

- hoặc trong VB: -

from t in TBL 
    group t by key = D 
    into g = group 
    select s = g.Sum(function(x) x.A), 
     m = g.Max(function(x) x.B), 
     a = g.Average(function(x) x.C) 

Rõ ràng, mà trong VB sẽ là:

aggregate t in TBL into s = Sum(t.A), m = Max(t.B), a = Average(t.C) 

mặc dù nó sẽ cho kết quả tương tự, nó có một chi phí cao hơn khi nó phát hành nhiều câu lệnh SQL chọn, một cho mỗi hàm tổng hợp, nghĩa là nó sẽ chạy trong nhiều lần truyền.Cú pháp đầu tiên, đưa ra một câu lệnh SQL đơn (khá phức tạp, nhưng hiệu quả) mà thực hiện một lần truyền duy nhất đối với cơ sở dữ liệu.

PS. Nếu bạn không có khóa để nhóm theo (nghĩa là bạn cần một hàng duy nhất là kết quả, bao gồm toàn bộ tập dữ liệu), hãy sử dụng hằng số như sau:

from t in TBL 
    group t by key = 0 
    into g = group 
    select s = g.Sum(function(x) x.A), 
     m = g.Max(function(x) x.B), 
     a = g.Average(function(x) x.C) 
+0

Điều này dường như hoạt động thực sự tốt. Cảm ơn bạn đã giải quyết điều này với một giải pháp tinh tế hơn và mạnh mẽ hơn so với sử dụng Entity SQL. Tôi tìm thấy bằng cách sử dụng 'nhóm t 1 vào g' làm việc tốt cho tôi. Cảm ơn một lần nữa! –

+0

Giải pháp được đề xuất là sử dụng nhóm, độ cao trong nhóm câu hỏi không được đề cập. . Nếu không có nhóm liên quan, tôi chỉ muốn thực hiện sql này: chọn tổng (A), max (B), avg (C) từ TBL? – Goran

+0

@Goran Được bao gồm bởi PS - bạn nhóm theo một hằng số. (Cảm thấy hơi kỳ lạ, nhưng có vẻ là câu trả lời kinh điển. Tôi cũng đã thấy nó được thực hiện trong các truy vấn SQL thô). – starwed

0

Rất tiếc, câu trả lời dường như là không.

Nếu ai đó có thể chứng minh nếu không, tôi sẽ sẵn lòng cấp cho họ câu trả lời được chấp nhận.


EDIT

Tôi tìm thấy một cách để làm điều này bằng Entity SQL. Tôi không chắc chắn đó là lớn nhất bằng cách nào, nhưng vì nó có vẻ là chỉ cách sau đó nó có thể là lớn nhất theo mặc định :)

var cmdText = "SELECT MIN(p.x), MAX(p.x), MIN(p.y), MAX(p.y) " + 
       "FROM Places AS p JOIN Logs AS l ON p.Id = l.PlaceId " + 
       "WHERE l.OwnerId==123"; 
var results = CreateQuery<DbDataRecord>(cmdText) 
var row = results.First(); 
var minX = (double)row[0]; 
var maxX = (double)row[1]; 
var minY = (double)row[2]; 
var maxY = (double)row[3]; 

Ở trên là không chính xác mã tôi làm việc với. Đối với một trường hợp đơn giản mà không có một tham gia, đây là SQL được tạo ra, cho thấy rằng chỉ có một chuyến đi đến DB được thực hiện:

SELECT 
1 AS [C1], 
[GroupBy1].[A1] AS [C2], 
[GroupBy1].[A2] AS [C3], 
[GroupBy1].[A3] AS [C4], 
[GroupBy1].[A4] AS [C5] 
FROM (SELECT 
    MIN([Extent1].[X1]) AS [A1], 
    MAX([Extent1].[X1]) AS [A2], 
    MAX([Extent1].[Y1]) AS [A3], 
    MIN([Extent1].[Y1]) AS [A4] 
    FROM [dbo].[Edges] AS [Extent1] 
    WHERE [Extent1].[PlaceId] = 123 
) AS [GroupBy1] 

Nếu ai đó tìm thấy một giải pháp thanh lịch hơn cho vấn đề này, tôi sẽ cấp cho họ câu trả lời được chấp nhận .


EDIT 2

Nhờ Costas người tìm thấy a great solution to this problem có sử dụng tinh khiết LINQ.

2

Tôi không có DB của bạn, nhưng điều này (sử dụng "mặc định" EDMX mô hình Northwind.mdb - không có thay đổi sau khi chạy wizard mô hình mới) chạy như một truy vấn trong LINQPad:

var one = from c in Customers 
      where c.PostalCode == "12209" 
      select new 
      { 
       Id = c.Orders.Max(o => o.OrderID), 
       Country = c.Orders.Min(o => o.ShipCountry) 
      };   

one.Dump(); 

cập nhật, mỗi bình luận của bạn:

var two = from c in Customers 
     where c.PostalCode == "12209" 
     from o in c.Orders 
     group o by o.Customer.PostalCode into g 
     select new 
     { 
      PostalCode = g.Key, 
      Id = g.Max(o => o.OrderID), 
      Country = g.Min(o => o.ShipCountry) 
     };   

two.Dump(); 
+0

Xin chào @Craig. Tôi không có Northwind trên máy tính của tôi, nhưng bằng nhãn cầu tôi nghĩ rằng truy vấn này sẽ tạo ra một mục nhập cho mỗi khách hàng có mã bưu điện đó. Những gì tôi muốn làm là tương đương với việc xác định OrderId tối đa cho bất kỳ khách hàng nào có mã bưu điện đó. Điều đó có thể không? Tôi đã đơn giản hóa câu hỏi ban đầu của mình có lẽ quá nhiều. Tôi sẽ cập nhật nó để làm cho nó phù hợp hơn với lược đồ thực tế của tôi. –

+0

OK, xem câu trả lời cập nhật. Điều này có thể được đơn giản hóa, nhưng tôi đã chọn theo mẫu của truy vấn đầu tiên. –

+0

trong trường hợp bạn quan tâm, tôi tìm thấy một giải pháp hoạt động, mặc dù nó không phải là đẹp nhất. –

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