2013-07-15 69 views
1

我有一個特定時間段(activationDate到endDate)的服務銷售清單。我需要按月份(例如2012年4月)生成銷售報告分組。對於每個月我想展示多少整月的使用和多少天。使用日期/月份分組處理列表中的數據

我的類:

public class SaleMonth 
{ 
    public DateTime MonthYear { get; set; }//.ToString("Y") 

    public int FullMonth { get; set; } 
    public int DaysMonth { get; set; } 

    public string TotalMonths { get { return String.Format("{0:N2}", 
            (((FullMonth * 30.5) + DaysMonth)/30.5)); } } 
} 

我曾嘗試:

using (CompanyContext db = new CompanyContext()) 
{ 
    var saleList = db.MySales.ToList(); 
    DateTime from = saleList.Min(s => s.ActivationDate), 
     to = saleList.Max(s => s.EndDate); 

    for (DateTime currDate = from.AddDays(-from.Day + 1) 
           .AddTicks(-from.TimeOfDay.Ticks); 
       currDate < to; 
       currDate = currDate.AddMonths(1)) 
    { 
     var sm = new SaleMonth 
     { 
      MonthYear = currDate, 
      FullMonth = 0, 
      DaysMonth = 0 
     }; 

     var monthSell = saleList.Where(p => p.ActivationDate < currDate.AddMonths(1) 
               || p.EndDate > currDate); 
     foreach (var sale in monthSell) 
     { 
     if (sale.ActivationDate.Month == sale.EndDate.Month 
      && sale.ActivationDate.Year == sale.EndDate.Year) 
     {//eg 4/6/13 - 17/6/13 
      sm.DaysMonth += (sale.EndDate.Day - sale.ActivationDate.Day + 1); 
     } 
     else 
     { 
      if (sale.ActivationDate.Year == currDate.Year 
        && sale.ActivationDate.Month == currDate.Month) 
       sm.DaysMonth += (currDate.AddMonths(1) - sale.ActivationDate).Days; 
      else if (sale.EndDate.Year == currDate.Year 
        && sale.EndDate.Month == currDate.Month) 
       sm.DaysMonth += sale.EndDate.Day; 
      else if(sale.ActivationDate.Date <= currDate 
        && sale.EndDate > currDate.AddMonths(1)) 
       sm.FullMonth++; 
      }        
     } 
     vm.SaleMonthList.Add(sm); 
    } 
} 

我有我失去了一些東西的感覺,必須有一個更優雅的方式來做到這一點。

Here is a picture顯示一些銷售和從他們產生的報告。

回答

3

LINQ確實包含一種將數據分組的方法。

// group by Year-Month 
var rows = from s in saleList 
    orderby s.MonthYear 
    group s by new { Year = s.MonthYear.Year, Month = s.MonthYear.Month }; 

上面的語句將你的數據和組它由年月,這樣它會爲每個年月組合主鍵,創建一組的是:通過本聲明考慮看看啓動相應的SaleMonth項目進入該組。

當您掌握這一點時,下一步就是使用這些組計算每組中要計算的任何內容。所以,如果你只是希望總所有FullMonthsDaysMonths每個年月,你可以這樣做:

var rowsTotals = from s in saleList 
    orderby s.MonthYear 
    group s by new { Year = s.MonthYear.Year, Month = s.MonthYear.Month } into grp 
    select new 
    { 
     YearMonth = grp.Key.Year + " " + grp.Key.Month, 
     FullMonthTotal = grp.Sum (x => x.FullMonth), 
     DaysMonthTotal = grp.Sum (x => x.DaysMonth) 
    }; 

編輯:

你在做什麼,再次尋找後,我認爲這樣做會更有效率:

// populate our class with the time period we are interested in 
var startDate = saleList.Min (x => x.ActivationDate); 
var endDate = saleList.Max (x => x.EndDate); 

List<SaleMonth> salesReport = new List<SaleMonth>(); 
for(var i = new DateTime(startDate.Year, startDate.Month, 1); 
    i <= new DateTime(endDate.Year, endDate.Month, 1); 
    i = i.AddMonths(1)) 
{ 
    salesReport.Add(new SaleMonth { MonthYear = i }); 
} 

// loop through each Month-Year 
foreach(var sr in salesReport) 
{ 
    // get all the sales that happen in this month 
    var lastDayThisMonth = sr.MonthYear.AddMonths(1).AddDays(-1); 
    var sales = from s in saleList 
     where lastDayThisMonth >= s.ActivationDate, 
     where sr.MonthYear <= s.EndDate 
    select s; 

    // calculate the number of days the sale spans for just this month 
    var nextMonth = sr.MonthYear.AddMonths(1); 
    var firstOfNextMonth = sr.MonthYear.AddMonths(1).AddDays(-1).Day; 
    sr.DaysMonth = sales.Sum (x => 
     (x.EndDate < nextMonth ? x.EndDate.Day : firstOfNextMonth) - 
      (sr.MonthYear > x.ActivationDate ? 
      sr.MonthYear.Day : x.ActivationDate.Day)); 

    // how many sales occur over the entire month 
    sr.FullMonth = sales.Where (x => x.ActivationDate <= sr.MonthYear && 
           nextMonth < x.EndDate).Count(); 
} 
+0

真的謝謝你!我喜歡填充日期的方式,然後通過它們並相應地獲取數據。 除了根據給定的日期檢索所有銷售對我其他地方非常有用 - 單擊報告中每行的「詳細信息」。 所以+1兩次=) – oCcSking

+0

嗨Ijust在Sum函數上得到這個錯誤..「只有無參數的構造函數和初始化程序在linq中支持實體」我嘗試將addMonth(1)更改爲外部var - monthLetter,但是stil get這個例外「你有空閒嗎? – oCcSking

+1

@oCcSking,對於Linq-to-Entities,您必須刪除所有正在創建的無參數構造函數。如果你看看我的編輯,我已經改變了應該讓你再次去的所有查詢,並且至少會讓你運行來進行調整。 –

1

我同意Rem先生的說法,LINQ是要走的路。由於你的計算是複雜的,我想創建一個輔助功能,以及:

Func<DateTime, DateTime, bool> matchMonth = (date1, date2) => 
    date1.Month == date2.Month && date1.Year == date2.Year; 

然後,您可以創建在計算中使用的函數:

Func<MySale, DateTime, int> calcDaysMonth = (sale, currDate) => 
{ 
    if (matchMonth(sale.ActivationDate, sale.EndDate)) 
    { 
      return (sale.EndDate.Day - sale.ActivationDate.Day + 1); 
    } 
    else 
    { 
     if (matchMonth(sale.ActivationDate, currDate)) 
      return (currDate.AddMonths(1) - sale.ActivationDate).Days; 
     else if (matchMonth(sale.EndDate, currDate) 
      return sale.EndDate.Day; 
     else 
      return 0; 
    } 
} 

如果用雷姆的先生結合這些技術,你應該有一個很好的,可讀的,簡潔的函數來爲你收集數據,並且很容易測試和調試。

+0

這是真實的,並在重用方面更優雅。我是在時間和空間 複雜性或改變像LINQ或一些其它的東西可愛:) 10倍梅索德反正方面尋找性能更改變的事實。 。 – oCcSking

相關問題