使用LINQ创建用于图表的年度摘要系列

时间:2013-07-29 19:06:43

标签: c# asp.net sql linq

我有一个基于可用数据的数据库程序,会给我一个分组的年度摘要。我正在运行我的测试实例,目前我的数据匹配类似的东西

--------------------------------------------
|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将其分成单独的系列并添加为我的图表系列

3 个答案:

答案 0 :(得分:0)

要获取空值,您应使用IEnumberable.DefaultIfEmpty()

进行左连接
/* omitted */
group occurence by g.Field<int>("TargetedBehaviorId") into occurences
from o in occurrences.DefaultIfEmpty()
select new
/* omitted */

睡觉后更新

我用它稍微涂了一点,但我没有找到一种优雅的方法来实现这一目标。

如果按TargetBehaviour分组,那么您只能获得发生某些事情的月份,而DefaultIfEmpty实际上不会产生影响。如果按Month进行分组,除非有行为,否则您将获得空出现的所有月份。

我会probalby使用第一个解决方案(从而呈现我的答案:完全不使用DefaultIfEmpty)并使用Union添加空白月份。无论您使用的是Entity Framework还是Linq-to-Sql,联合都可以提供麻烦(在EF中)并且需要在内存中执行。

因此,对于无关紧要的人,你应该看看 jocull 的答案。

答案 1 :(得分:0)

为了更好地理解,使用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
    };
});

答案 2 :(得分:-1)

如果你想在SQL端工作,你可以使用SQL程序,一切都有它的优点和缺点,你将要使用的是你自己。