使用LINQ创建年度汇总系列以用于图表制作
本文关键字:用于 系列 创建 LINQ 使用 | 更新日期: 2023-09-27 18:25:28
我有一个数据库过程,根据可用的数据,它将给我一个分组的年度摘要。对于我正在运行的测试实例,目前我的数据与类似的相匹配
--------------------------------------------
|Month |Id |BehaviorName |Count |
--------------------------------------------
|1 |NULL| |0 |
--------------------------------------------
|2 |NULL| |0 |
--------------------------------------------
|3 |NULL| |0 |
--------------------------------------------
|4 |NULL| |0 |
--------------------------------------------
|5 |NULL| |0 |
--------------------------------------------
|6 |4 |Name1 |2 |
--------------------------------------------
|7 |4 |Name1 |5 |
--------------------------------------------
|7 |3 |NameElse |7 |
--------------------------------------------
|8 |NULL| |0 |
--------------------------------------------
|9 |NULL| |0 |
--------------------------------------------
|10 |NULL| |0 |
--------------------------------------------
|11 |NULL| |0 |
--------------------------------------------
|12 |NULL| |0 |
--------------------------------------------
这是一个非常简单的结果集,但数据可能或多或少复杂。此处输出的数据将与Telerik RadChart控件一起用于绘图目的。本质上,我需要做的是获取上面的结果集,并根据指定ID的实例数量对其进行镜像,以便将其绘制为单独的系列
例如,名为Name1的BehaviorName会有自己的结果集,比如
--------------------------------------------
|Month |Id |BehaviorName |Count |
--------------------------------------------
|1 |NULL| |0 |
--------------------------------------------
|2 |NULL| |0 |
--------------------------------------------
|3 |NULL| |0 |
--------------------------------------------
|4 |NULL| |0 |
--------------------------------------------
|5 |NULL| |0 |
--------------------------------------------
|6 |4 |Name1 |2 |
--------------------------------------------
|7 |4 |Name1 |5 |
--------------------------------------------
|8 |NULL| |0 |
--------------------------------------------
|9 |NULL| |0 |
--------------------------------------------
|10 |NULL| |0 |
--------------------------------------------
|11 |NULL| |0 |
--------------------------------------------
|12 |NULL| |0 |
--------------------------------------------
名为NameElse的BehaviorName会有自己的结果集,比如
--------------------------------------------
|Month |Id |BehaviorName |Count |
--------------------------------------------
|1 |NULL| |0 |
--------------------------------------------
|2 |NULL| |0 |
--------------------------------------------
|3 |NULL| |0 |
--------------------------------------------
|4 |NULL| |0 |
--------------------------------------------
|5 |NULL| |0 |
--------------------------------------------
|6 |NULL| |0 |
--------------------------------------------
|7 |3 |NameElse |7 |
--------------------------------------------
|8 |NULL| |0 |
--------------------------------------------
|9 |NULL| |0 |
--------------------------------------------
|10 |NULL| |0 |
--------------------------------------------
|11 |NULL| |0 |
--------------------------------------------
|12 |NULL| |0 |
--------------------------------------------
在我的应用程序中,我决定采用单个结果集,并尝试通过LINQ将其拆分为单独的分组。我的问题是,最好的方法是什么?
var series = from occurence in fetched.YearlyTotals.Tables[0].AsEnumerable()
group occurence by g.Field<int>("TargetedBehaviorId") into occurences
select new
{
Behavior = occurences.Key,
Occurences = from o in occurences
select new
{
Month = o.Field<int>("Month"),
TargetedBehaviorId = o.Field<int>("TargetedBehaviorId"),
BehaviorName = o.Field<string>("BehaviorName"),
Count = o.Field<int>("Count")
}
};
目前,这将忽略任何空字段,但我需要在序列中的每个月都包括与上面的结果集匹配的每个唯一行。目前,这个LINQ语句只给了我3行数据。
此外,如果有一种更简单的方法可以在SQL端实现这一点,我也不会反对使用它。
更新:
我最终使用了这个问题中提到的一些技巧的组合。我创建了一个专门用于创建图表系列项目的类,只是为了清晰
private class OccurrenceItem
{
public int TargetedBehaviorId { get; set; }
public int Month { get; set; }
public int Count { get; set; }
public string BehaviorName { get; set; }
public OccurrenceItem()
{
}
}
//Gets a list of all the months that are returne from the query
//Always returns all 12 months
int[] months = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
var currentBehaviors = from behaviors in fetched.TargetedBehaviors
select behaviors;
var emptySet = (from month in months
from behavior in currentBehaviors
select new OccurrenceItem
{
Month = month,
TargetedBehaviorId = behavior.AssignedBehaviorId,
BehaviorName = behavior.Behavior,
Count = 0
}).ToList();
var occurences = (from o in fetched.YearlyTotals.Tables[0].AsEnumerable()
select new OccurrenceItem {
Month = o.Field<int>("Month"),
TargetedBehaviorId = o.Field<int>("TargetedBehaviorId"),
BehaviorName = o.Field<string>("BehaviorName"),
Count = o.Field<int>("Count")
}).ToList();
var merged = occurences.Union(emptySet)
.GroupBy(x => new {
x.Month,
x.TargetedBehaviorId,
x.BehaviorName,
}).Select(x => new OccurrenceItem {
Month = x.Key.Month,
TargetedBehaviorId = x.Key.TargetedBehaviorId,
BehaviorName = x.Key.BehaviorName,
Count = (from y in x
select y.Count).Sum()
}).OrderBy(x => x.Month);
所有这些语句加在一起给我一个List<OccurenceItem>
,然后我可以根据TargetedBehaviorId
将其分离成单独的系列,并添加为我的图表系列
为了更好地理解,用IEnumerables或IQueryables分解步骤可能是值得的。你可以尝试这样的方法(未经测试,语法不确定),以便有一个明确定义的月份列表,以确保即使没有设置,也能得到所有月份。在没有数据的情况下,你可以返回一个自定义的默认形式-无论你想它是什么
int[] months = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
var series = months.SelectMany(m => {
var query = from occurence in fetched.YearlyTotals.Tables[0].AsEnumerable()
where occurence.Field<int>("Month") == m
select occurence;
var grouped = query.GroupBy(x => x.Field<int>("TargetedBehaviorId"));
if(grouped.Any())
{
return from g in grouped
select new
{
Month = m,
Behavior = occurences.Key,
Occurences = from o in occurences
select new
{
Month = o.Field<int>("Month"),
TargetedBehaviorId = o.Field<int>("TargetedBehaviorId"),
BehaviorName = o.Field<string>("BehaviorName"),
Count = o.Field<int>("Count")
}
};
}
//Default for the month
return new {
Month = m,
Behavior = int.MinValue,
Occurences = null
};
});
要获得null值,您应该使用IEnumberable.DefaultIfEmpty()进行左联接
/* omitted */
group occurence by g.Field<int>("TargetedBehaviorId") into occurences
from o in occurrences.DefaultIfEmpty()
select new
/* omitted */
睡眠后更新
我花了一点时间,但我没有找到一种优雅的方式来实现这一点。
如果你按TargetBehaviour
分组,那么你只会得到发生某些事情的月份,而DefaultIfEmpty
实际上不会产生影响。如果你按Month
分组,你将得到除有行为外所有月份都是空的。
我可能会使用第一个解决方案(因此给出了我的答案:完全不正确地使用DefaultIfEmpty),并使用Union
添加空月份。取决于您是使用Entity Framework还是使用Linq to Sql,联合可能会带来麻烦(在EF中),并且需要在内存中执行。
因此,很抱歉,您可能应该调查一下jocurl的答案。
如果你想在SQL端工作,你可以使用SQL过程,一切都有其优点和缺点,你将使用什么取决于你自己。