我有一个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
}));
如果我有12/1 - 12/4/2009的数据(简化):
12/1/2009 20
12/2/2009 15
12/4/2009 16
我希望通过代码添加 12/3/2009 0 条目。
我知道通常这应该在数据库中使用非规范化表来完成,您可以使用数据填充数据或加入日历表,但我的问题是如何在代码中完成此操作?
可以在Linq完成吗?应该在Linq中完成吗?
答案 0 :(得分:1)
基本上我最终在这里做的是创建一个相同类型的列表,其中包含范围内的所有日期和计数的0值。然后将我原始查询的结果与此列表结合使用。主要障碍是创建自定义IEqualityComparer。有关详细信息,请访问:click here
答案 1 :(得分:1)
我今天就这样做了。我从数据库中收集了完整的数据,然后生成了一个“样本空”表。最后,我使用真实数据对空表进行外连接,并使用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();
答案 2 :(得分:0)
您可以生成从“开始”开始到“结束”结束的日期列表,然后逐步检查每个日期的计数数量
答案 3 :(得分:0)
我创建了一个辅助函数,它被设计用于匿名类型,并以尽可能通用的方式重用。
让我们说这是您获取每个日期的订单列表的查询。
var orders = db.Orders
.GroupBy(o => o.OrderDate)
.Select(o => new
{
OrderDate = o.Key,
OrderCount = o.Count(),
Sales = o.Sum(i => i.SubTotal)
}
.OrderBy(o => o.OrderDate);
我的功能请注意,此列表必须按日期排序。如果我们有一天没有销售,那么列表中就会有一个漏洞。
现在为将使用默认值(匿名类型的实例)填充空白的函数。
private static IEnumerable<T> FillInEmptyDates<T>(IEnumerable<DateTime> allDates, IEnumerable<T> sourceData, Func<T, DateTime> dateSelector, Func<DateTime, T> defaultItemFactory)
{
// iterate through the source collection
var iterator = sourceData.GetEnumerator();
iterator.MoveNext();
// for each date in the desired list
foreach (var desiredDate in allDates)
{
// check if the current item exists and is the 'desired' date
if (iterator.Current != null &&
dateSelector(iterator.Current) == desiredDate)
{
// if so then return it and move to the next item
yield return iterator.Current;
iterator.MoveNext();
// if source data is now exhausted then continue
if (iterator.Current == null)
{
continue;
}
// ensure next item is not a duplicate
if (dateSelector(iterator.Current) == desiredDate)
{
throw new Exception("More than one item found in source collection with date " + desiredDate);
}
}
else
{
// if the current 'desired' item doesn't exist then
// create a dummy item using the provided factory
yield return defaultItemFactory(desiredDate);
}
}
}
用法如下:
// first you must determine your desired list of dates which must be in order
// determine this however you want
var desiredDates = ....;
// fill in any holes
var ordersByDate = FillInEmptyDates(desiredDates,
// Source list (with holes)
orders,
// How do we get a date from an order
(order) => order.OrderDate,
// How do we create an 'empty' item
(date) => new
{
OrderDate = date,
OrderCount = 0,
Sales = 0
});
desiredDates
和sourceData
必须按顺序sourceData
中包含了对重复项目的检查,但desiredDates