2010-05-16 47 views
2

我目前有以下代碼可在過去30天內生成銷售報告。我想知道是否可以使用linq來一步生成這個報告,而不是我在這裏的基本循環。使用Linq to SQL生成銷售報告

對於我的要求,每天都需要給我返回一個值,所以如果在任何一天沒有銷售,那麼返回0。

任何Sum linq示例在那裏都沒有解釋如何包含where過濾器,所以我很困惑如何獲得每天的總金額或0,如果沒有銷售,最後我經歷的日子。

感謝您的幫助, 豐富

//setup date ranges to use 
    DateTime startDate = DateTime.Now.AddDays(-29); 
    DateTime endDate = DateTime.Now.AddDays(1); 
    TimeSpan startTS = new TimeSpan(0, 0, 0); 
    TimeSpan endTS = new TimeSpan(23, 59, 59); 

    using (var dc = new DataContext()) 
    { 
     //get database sales from 29 days ago at midnight to the end of today 
     var salesForDay = dc.Orders.Where(b => b.OrderDateTime > Convert.ToDateTime(startDate.Date + startTS) && b.OrderDateTime <= Convert.ToDateTime(endDate.Date + endTS)); 

     //loop through each day and sum up the total orders, if none then set to 0 
     while (startDate != endDate) 
     { 
      decimal totalSales = 0m; 
      DateTime startDay = startDate.Date + startTS; 
      DateTime endDay = startDate.Date + endTS; 
      foreach (var sale in salesForDay.Where(b => b.OrderDateTime > startDay && b.OrderDateTime <= endDay)) 
      { 
       totalSales += (decimal)sale.OrderPrice; 
      } 

      Response.Write("From Date: " + startDay + " - To Date: " + endDay + ". Sales: " + String.Format("{0:0.00}", totalSales) + "<br>"); 

      //move to next day 
      startDate = startDate.AddDays(1); 
     } 
    } 

編輯: 約翰的回答是處理我的查詢的好方法。以下是對代碼進行調整以使其適用於此示例,以防其他人遇到此問題。這將從allDays表執行外連接,並在當天沒有銷售時返回0值。

var query = from d in allDays 
        join s in salesByDay on d equals s.Day into j 
        from s in j.DefaultIfEmpty() 
        select new { Day = d, totalSales = (s != null) ? s.totalSales : 0m }; 

回答

4

您可以將所有數據按天分組,然後對這些組進行求和。爲了滿足每天有一定金額的要求,即使那些沒有訂單的人也可以加入所有日期的列表,或者簡單地使用循環來確保包含所有日期。小提示:如果您通過DateTime.Date屬性進行比較,則不需要明確設置時間。

下面是一個使用生成器功能(從MoreLinq項目採取)的解決方案:

public static partial class MoreEnumerable 
{ 

    public static IEnumerable<TResult> GenerateByIndex<TResult>(Func<int, TResult> generator) 
    { 
     // Looping over 0...int.MaxValue inclusive is a pain. Simplest is to go exclusive, 
     // then go again for int.MaxValue. 
     for (int i = 0; i < int.MaxValue; i++) 
     { 
      yield return generator(i); 
     } 
     yield return generator(int.MaxValue); 
    } 

} 

public class MyClass 
{ 
    private void test() 
    { 
     DateTime startDate = DateTime.Now.AddDays(-29); 
     DateTime endDate = DateTime.Now.AddDays(1); 

     using (var dc = new DataContext()) 
     { 
      //get database sales from 29 days ago at midnight to the end of today 
      var salesForPeriod = dc.Orders.Where(b => b.OrderDateTime > startDate.Date && b.OrderDateTime <= endDate.Date); 

      var allDays = MoreEnumerable.GenerateByIndex(i => startDate.AddDays(i)).Take(30); 

      var salesByDay = from s in salesForPeriod 
         group s by s.OrderDateTime.Date into g 
         select new {Day = g.Key, totalSales = g.Sum(x=>(decimal)x.OrderPrice}; 

      var query = from d in allDays 
         join s in salesByDay on s.Day equals d 
         select new {Day = s.Day , totalSales = (s != null) ? s.totalSales : 0m; 


      foreach (var item in query) 
      { 
       Response.Write("Date: " +item.Day.ToString() " Sales: " + String.Format("{0:0.00}", item.totalSales) + "<br>"); 
      } 


     } 
    } 
} 
+0

我與解決方案上來過,但請求似乎包括爲其返回任何行天。在這種情況下,這一天的價值應該爲零。 使用查詢結果,我可以遍歷每一天,並檢查結果是否包含當天的總和。如果是這樣,我會退還這筆款項。否則,我會返回零。但是,這不是按要求的單步LINQ查詢。 – 2010-05-16 19:22:18

+0

我看到你的編輯。非常好。 – 2010-05-16 19:40:05

+0

@Jimmy:謝謝,但我認爲這個更好。 – 2010-05-16 19:45:53

0

我認爲如果你的枚舉不包含一天的數據,那麼你不能返回那一天的值。我能想到的最好的方法是創建一個每天有零值的Order對象列表,並創建一個包含查詢結果的聯合。這是我想出來的。但是,我認爲循環遍歷每個組,檢查是否有任何一天被「跳過」,並且每天「跳過」返回零比在內存中創建自己的枚舉更直接(除非您想要列舉具有「缺失的空位「 填寫)。請注意,我基本上假設,對於每個組,您想要將所有值彙總爲一天。

List<Order> zeroList = new List<Order>(); 
while (startDate <= endDate) 
{ 
    zeroList.Add(new Order { OrderDateTime = startDate, OrderPrice = 0 }); 
    startDate = startDate.AddDays(1) 
} 

var comboList = zeroList.Union(dc.Orders.Where(b => b.OrderDateTime > Convert.ToDateTime(startDate.Date + startTS) && b.OrderDateTime <= Convert.ToDateTime(endDate.Date + endTS)) 

var groupedTotalSales = comboList.GroupBy(b => b.OrderDateTime.Date) 
    .Select(b => new { StartDate = Convert.ToDateTime(b.Key + startTS), EndDate = Convert.ToDateTime(b.Key + endTS), Sum = b.Sum(x => x.OrderPrice }); 

foreach (totalSale in groupedTotalSales) 
    Response.Write("From Date: " + totalSale.StartDate + " - To Date: " + totalSale.EndDate + ". Sales: " + String.Format("{0:0.00}", (decimal)totalSale.Sum) + "<br/>");