2012-03-27 38 views
10

Tôi đã thực hiện một chút nghiên cứu về điều này và tốt nhất tôi đã tìm thấy cho đến nay là sử dụng Asenumerable trên toàn bộ tập dữ liệu, để lọc xảy ra trong LINQ đối tượng thay vì hơn cả DB. Tôi đang sử dụng EF mới nhất.Entity Framework: Phân nhóm hiệu quả theo tháng

tôi làm việc (nhưng rất chậm) mã là:

 var trendData = 
      from d in ExpenseItemsViewableDirect.AsEnumerable() 
      group d by new {Period = d.Er_Approved_Date.Year.ToString() + "-" + d.Er_Approved_Date.Month.ToString("00") } into g 
      select new 
      { 
       Period = g.Key.Period, 
       Total = g.Sum(x => x.Item_Amount), 
       AveragePerTrans = Math.Round(g.Average(x => x.Item_Amount),2) 
      }; 

này mang lại cho tôi tháng ở định dạng YYYY-MM, cùng với tổng số tiền và số tiền trung bình. Tuy nhiên phải mất vài phút mỗi lần.

Cách giải quyết khác của tôi là thực hiện truy vấn cập nhật trong SQL vì vậy tôi có trường YYYYMM để nhóm theo nguyên bản. Thay đổi DB không phải là một sửa chữa dễ dàng tuy nhiên vì vậy bất kỳ đề xuất sẽ được đánh giá cao.

Chủ đề tôi tìm thấy ý tưởng mã trên (http://stackoverflow.com/questions/1059737/group-by-weeks-in-linq-to-entities) đề cập đến 'chờ cho đến khi .NET 4.0'. Có điều gì gần đây được giới thiệu giúp trong tình huống này không?

Trả lời

14

Lý do hiệu suất kém là toàn bộ bảng được tìm nạp vào bộ nhớ (AsEnumerable()). Bạn có thể nhóm sau đó bởi Năm và Tháng như thế này

var trendData = 
      (from d in ExpenseItemsViewableDirect 
      group d by new { 
          Year = d.Er_Approved_Date.Year, 
          Month = d.Er_Approved_Date.Month 
          } into g 
      select new 
      { 
       Year = g.Key.Year, 
       Month = g.Key.Month, 
       Total = g.Sum(x => x.Item_Amount), 
       AveragePerTrans = Math.Round(g.Average(x => x.Item_Amount),2) 
      } 
     ).AsEnumerable() 
     .Select(g=>new { 
       Period = g.Year + "-" + g.Month, 
       Total = g.Total, 
       AveragePerTrans = g.AveragePerTrans 
     }); 

chỉnh sửa

Các truy vấn ban đầu, từ phản ứng của tôi, đã cố gắng để làm một nối giữa int và một chuỗi, mà không phải là dịch bởi EF thành câu lệnh SQL. Tôi có thể sử dụng SqlFunctions lớp học, nhưng truy vấn nó được loại xấu xí. Vì vậy, tôi đã thêm AsEnumerable() sau khi nhóm được thực hiện, điều đó có nghĩa là EF sẽ thực hiện truy vấn nhóm trên máy chủ, sẽ nhận được năm, tháng, v.v ... nhưng chiếu tùy chỉnh được thực hiện trên đối tượng (theo sau AsEnumerable()).

+1

Tuyệt vời, cảm ơn nhiều Adrian. Tôi vừa làm một bài kiểm tra nhanh và bạn mất khoảng 3,5 giây so với bản 5.2 cho bản gốc. Sự chậm trễ lâu dài phải là một số bước khác trong chương trình của tôi. Tôi đánh giá cao nỗ lực của bạn, mã của bạn đã được đưa vào hoạt động! – Glinkot

+0

Đây không phải là câu trả lời được chấp nhận. Đây chỉ là một cách giải quyết! Câu trả lời từ @cryss cho kết quả hoàn hảo. –

+0

Giải pháp tuyệt vời, "nhóm theo mới" thực sự mạnh mẽ và đơn giản hóa các truy vấn phức tạp. – Jacob

8

Khi nói đến nhóm theo tháng tôi thích làm công việc này theo cách này:

var sqlMinDate = (DateTime) SqlDateTime.MinValue; 

var trendData = ExpenseItemsViewableDirect 
    .GroupBy(x => SqlFunctions.DateAdd("month", SqlFunctions.DateDiff("month", sqlMinDate, x.Er_Approved_Date), sqlMinDate)) 
    .Select(x => new 
    { 
     Period = g.Key // DateTime type 
    }) 

Như nó giữ kiểu datetime trong kết quả nhóm.

+1

Điều này chắc chắn sẽ là câu trả lời được chấp nhận! Sự phân tách tháng MS-SQL đơn giản được dịch thành linq-to-entity! –

2

Tương tự như những gì cryss đã viết, tôi đang làm như sau cho EF. Lưu ý chúng ta phải sử dụng EntityFunctions để có thể gọi tất cả các nhà cung cấp DB được hỗ trợ bởi EF. SqlFunctions chỉ hoạt động cho SQLServer.

var sqlMinDate = (DateTime) SqlDateTime.MinValue; 

(from x in ExpenseItemsViewableDirect 
let month = EntityFunctions.AddMonths(sqlMinDate, EntityFunctions.DiffMonths(sqlMinDate, x.Er_Approved_Date)) 
group d by month 
into g 
select new 
{ 
Period = g.Key, 
    Total = g.Sum(x => x.Item_Amount), 
    AveragePerTrans = Math.Round(g.Average(x => x.Item_Amount),2) 
}).Dump(); 

Một hương vị của SQL được tạo ra (từ một schema tương tự):

-- Region Parameters 
DECLARE @p__linq__0 DateTime2 = '1753-01-01 00:00:00.0000000' 
DECLARE @p__linq__1 DateTime2 = '1753-01-01 00:00:00.0000000' 
-- EndRegion 
SELECT 
1 AS [C1], 
[GroupBy1].[K1] AS [C2], 
[GroupBy1].[A1] AS [C3] 
FROM (SELECT 
    [Project1].[C1] AS [K1], 
    FROM (SELECT 
     DATEADD (month, DATEDIFF (month, @p__linq__1, [Extent1].[CreationDate]), @p__linq__0) AS [C1] 
     FROM [YourTable] AS [Extent1] 
    ) AS [Project1] 
    GROUP BY [Project1].[C1] 
) AS [GroupBy1] 
Các vấn đề liên quan