根据时间范围获取结果并按月分组

时间:2015-08-18 15:47:41

标签: c# linq sql-server-2008

我有一个包含许多表的数据库。其中一个表VersionBuild包含一个日期列。我使用以下查询获得结果,并且仅选择在特定时间范围内出现的记录(例如,过去6个月)

我使用的代码如下

DateTime startDate = DateTime.Now.AddMonths(-(timeFrame));
var grouped = db.VersionBuilds
                .Where(r => r.product_id == id && r.date > startDate)
                .GroupBy(r => new { r.date.Year, r.date.Month })
                .Select(r => new { 
                    month = getMonthName(r.Key.Month), 
                    totalIssues = r.Sum(zz => zz.AcrolinxReport.total_issues) 
                });
return grouped;

代码的一个问题是,如果某个月没有记录,它会忽略它。我在图表中显示返回的信息,这样就会在数据中留下空隙。例如,如果我在5月和6月有记录,它将返回may和june的数据,但我想要的是返回指定时间范围内所有月份的数据,无论是否有任何数据。

2 个答案:

答案 0 :(得分:1)

您可以尝试这样的事情:

首先,我们创建一个包含给定时间范围内所有月份的列表 例如,如果我们现在运行它并将timeFrame设置为3,那么 此列表将包含以下日期时间

  • 2015-08-15 ....,
  • 2015-07-15 ....,
  • 2015年6月15日

以下是代码:

var monthsInTimeFrame = new List<DateTime>();
for(int i = timeFrame; i>=0 ; i--)
{ 
    monthsInTimeFrame.Add(DateTime.Now.AddMonths(-i));
}

然后你继续以你已经穹顶的方式获取数据, 但是有一点变化。而不是创建一个对象列表 匿名类型,您创建一个命名类型的对象列表。我选择了模糊的名字Data。你可以选择你想要的任何名字。此外,此类型还有两个额外属性,一个是Year,另一个是Month。我们需要它们,以便确定预期月份中的特定月份是否在列表中。

DateTime startDate = DateTime.Now.AddMonths(-(timeFrame));
var grouped = db.VersionBuilds
                .Where(r => r.product_id == id && r.date > startDate)
                .GroupBy(r => new { r.date.Year, r.date.Month })
                .Select(r => new Data
                { 
                    Year = r.Key.Name,
                    Month = r.Key.Month,
                    MonthName = getMonthName(r.Key.Month), 
                    TotalIssues = r.Sum(zz => zz.AcrolinxReport.total_issues) 
                }).ToList();

请注意,我们必须在查询结束时调用ToList,因为下面我们将使用Add方法添加丢失的月份。

最后,我们必须遍历monthsInTimeFrame

的项目
foreach(var month in monthsInTimeFrame)
{
    // If there isn't any item in the list called
    // grouped with the same year and month of the current month,
    // we have to add a new item in this list       
    if(!grouped.Any(item=>item.Year==month.Year &&
                          item.Month==month.Month)) 
        grouped.Add(new Data
        { 
            Year = month.Year, 
            Month = month.Month,
            MonthName = getMonthName(month.Month), 
            TotalIssues = 0 
        });
}
return grouped;

Data类的定义可能是这样的:

public class Data
{
    public int Year { get; set; }
    public int Month { get; set; }
    public string MonthName { get; set; }
    public int TotalIssues { get; set; }
}

答案 1 :(得分:1)

我相信这对你有用。没有数据就很难测试,但我认为你不应该有任何问题。

// Assume 'timeframe' is a positive integer.

// Get the start date.
DateTime startDate = DateTime.Now.AddMonths(-timeframe);

// Generate a list of months (year and month, actually) beginning 
// with the month of 'startDate'.
var months =
    Enumerable
    .Range(0, timeframe)
    .Select(x => startDate.AddMonths(x))
    .Select(x => new { x.Year, x.Month });

// Create builds query.
var builds =
    db.VersionBuilds
    .Where(r => r.product_id == id && r.date > startDate)
    .GroupBy(r => new { r.date.Year, r.date.Month })
    .Select(r => new { 
        Month = r.Key.Month,
        Year = r.Key.Year,
        TotalIssues = r.Sum(zz => zz.AcrolinxReport.total_issues));

// Join 'months' on 'builds'
var grouped =
    months
    .GroupJoin(
        builds.AsEnumerable(),
        date => date,
        build => new { build.Year, build.Month },
        (date, buildGroup) => new { 
            date.Year, 
            date.Month, 
            TotalIssues = buildGroup.Sum(x => x.TotalIssues) })
    .OrderBy(x => x.Year)
    .ThenBy(x => x.Month)
    .Select(x => new { Month = getMonth(x.Month), x.TotalIssues });