使用linq组按日期查询填写缺少日期

Gre*_*rts 8 c# linq datetime group-by

我有一个Linq查询,它基本上计算在特定日期创建的条目数,这是通过按年,月,日进行分组来完成的.问题是,因为有些日子没有任何条目我需要用0计数的条目来填补缺少的"日历日".我的猜测是,这可能是通过联盟或其他东西完成的,或者甚至可能是一些简单的for循环来处理查询后的记录.

这是查询:

from l in context.LoginToken
 where l.CreatedOn >= start && l.CreatedOn <= finish
 group l by
 new{l.CreatedOn.Year, l.CreatedOn.Month, l.CreatedOn.Day} into groups
 orderby groups.Key.Year , groups.Key.Month , groups.Key.Day
     select new StatsDateWithCount {
                                    Count = groups.Count(),
                                     Year =  groups.Key.Year,
                                    Month = groups.Key.Month,
                                      Day = groups.Key.Day
                                                                  }));
Run Code Online (Sandbox Code Playgroud)

如果我有12/1 - 12/4/2009的数据(简化):

12/1/2009 20
12/2/2009 15
12/4/2009 16
Run Code Online (Sandbox Code Playgroud)

我希望通过代码添加12/3/2009 0的条目.

我知道通常这应该在数据库中使用非规范化表来完成,您可以使用数据填充或加入日历表,但我的问题是如何在代码中完成此操作?
可以在Linq完成吗?应该在Linq完成吗?

小智 2

我今天刚做了这个。我从数据库中收集了完整的数据,然后生成了一个“样本空”表。最后,我对空表与真实数据进行了外连接,并使用 DefaultIfEmpty() 构造来了解数据库中何时缺少一行,并用默认值填充它。

这是我的代码:

int days = 30;

// Gather the data we have in the database, which will be incomplete for the graph (i.e. missing dates/subsystems).
var dataQuery =
    from tr in SourceDataTable
    where (DateTime.UtcNow - tr.CreatedTime).Days < 30
    group tr by new { tr.CreatedTime.Date, tr.Subsystem } into g
    orderby g.Key.Date ascending, g.Key.SubSystem ascending
    select new MyResults()
    {
        Date = g.Key.Date, 
        SubSystem = g.Key.SubSystem,
        Count = g.Count()
    };

// Generate the list of subsystems we want.
var subsystems = new[] { SubSystem.Foo, SubSystem.Bar }.AsQueryable();

// Generate the list of Dates we want.
var datetimes = new List<DateTime>();
for (int i = 0; i < days; i++)
{
    datetimes.Add(DateTime.UtcNow.AddDays(-i).Date);
}

// Generate the empty table, which is the shape of the output we want but without counts.
var emptyTableQuery =
    from dt in datetimes
    from subsys in subsystems
    select new MyResults()
    {
        Date = dt.Date, 
        SubSystem = subsys,
        Count = 0
    };

// Perform an outer join of the empty table with the real data and use the magic DefaultIfEmpty
// to handle the "there's no data from the database case".
var finalQuery =
    from e in emptyTableQuery
    join realData in dataQuery on 
        new { e.Date, e.SubSystem } equals 
        new { realData.Date, realData.SubSystem } into g
    from realDataJoin in g.DefaultIfEmpty()
    select new MyResults()
    {
        Date = e.Date,
        SubSystem = e.SubSystem,
        Count = realDataJoin == null ? 0 : realDataJoin.Count
    };

return finalQuery.OrderBy(x => x.Date).AsEnumerable();
Run Code Online (Sandbox Code Playgroud)