LINQ 的嵌套组

Posted

技术标签:

【中文标题】LINQ 的嵌套组【英文标题】:Nested Group by LINQ 【发布时间】:2012-03-06 06:21:48 【问题描述】:

我无法使用 LINQ 查询解决此问题。

所以我们的表结构如下: 编号 ||错误类别 ||错误名称 ||错误详细信息 || bug_priority

我想先按 bug_category 分组。对于每个 bug_category,我想依次按 bug__priority 分组。

所以基本上我想要类似的东西:

bug_category = AUDIO :: No of BUGS --> 严重 = 3,中等 = 2 和低 = 7 个错误。 bug_category = VIDEO :: No of BUGS --> 严重 = 5,中等 = 1 和低 = 9 个错误。

以下查询返回类别 AND customer_priority 的所有唯一组合:

(其中 RawDataList 只是具有上述结构的数据列表)

        var ProceesedData = from d in RawDataList
                      group d by new  d.bug_category, d.bug_priority  into g
                      select new
                      
                          g.Key.bug_category,
                          g.Key.bug_priority
                      ;

以下查询返回类别,后跟该类别中的记录列表:

            var ProceesedData = from d in RawDataList
                      group d by d.bug_category into g
                      select new  g.Key, records = g
                      ;

但我无法继续进行,因为 ProcessedData(返回变量)是未知类型。对此有什么想法吗?

【问题讨论】:

【参考方案1】:

这是完成嵌套分组的一种更简单的方法。我已经在内存集合中对其进行了测试,您的特定数据库提供商是否能很好地处理它可能会有所不同,或者它是否表现良好是未知的。

假设您有两个属性,并且想按州和国家/地区分组:

var grouped = People
  .GroupBy(l => new  l.State, l.Country)//group by two things
  .GroupBy(l=> l.Key.Country)//this will become the outer grouping


foreach(var country in grouped)

  foreach(var state in country)
  
     foreach(var personInState in state)
     
       string description = $"Name: personInState.Name, State: state.StateCode, Country: country.CountryCode";
       ...

     
  

【讨论】:

这是一个很难找到的答案! 是的,当我自己需要这个时,我花了一段时间才弄清楚这一点,当我需要这样做时,我实际上会回到自己的答案。【参考方案2】:

我怀疑你想要(名字改得更惯用了):

var query = from bug in RawListData
            group bug by new  bug.Category, bug.Priority  into grouped
            select new  
                Category = grouped.Key.Category,
                Priority = grouped.Key.Priority,
                Count = grouped.Count()
            ;

然后:

foreach (var result in query)

    Console.WriteLine("0 - 1 - 2",
                      result.Category, result.Priority, result.Count);

或者(但见后文):

var query = from bug in RawListData
            group bug by new bug.Category into grouped
            select new  
                Category = grouped.Category,
                Counts = from bug in grouped
                         group bug by grouped.Priority into g2
                         select new  Priority = g2.Key, Count = g2.Count() 
            ;

foreach (var result in query)

    Console.WriteLine("0: ", result.Category);
    foreach (var subresult in result.Counts)
    
        Console.WriteLine("  0: 1", subresult.Priority, subresult.Count);
    

编辑:如 cmets 中所述,这将导致多个 SQL 查询。要获得类似的结果结构但更有效,您可以使用:

var dbQuery = from bug in RawListData
              group bug by new  bug.Category, bug.Priority  into grouped
              select new  
                  Category = grouped.Key.Category,
                  Priority = grouped.Key.Priority,
                  Count = grouped.Count()
              ;

var query = dbQuery.ToLookup(result => result.Category,
                             result => new  result.Priority, result.Count ;


foreach (var result in query)

    Console.WriteLine("0: ", result.Key);
    foreach (var subresult in result)
    
        Console.WriteLine("  0: 1", subresult.Priority, subresult.Count);
    

【讨论】:

第二个查询将为每个组创建一个辅助查询,所以我认为更好的解决方案是第一个查询,然后是 ToLookup by category @AdrianIftode:你试过了吗?我没有,但我预计会生成适当的 SQL 以一次性完成所有操作。 是的,使用 LinqPad,一个查询类别组和其他 X 查询优先级组 @AdrianIftode:对 - 届时将对其进行编辑。我想知道在 EF 中是否会发生同样的事情...... 不,它在主要组和次要组之间进行外部应用(在外部应用连接中使用主要组键),因此在这种情况下为 EF +1(与许多其他情况一样)跨度> 【参考方案3】:

我认为您正在搜索类似的内容:

    var processedData =
        rawData.GroupBy(bugs => bugs.bug_category,
            (category, elements) =>
            new
                
                    Category = category,
                    Bugs = elements.GroupBy(bugs => bugs.bug_priority,
                                        (priority, realbugs) =>
                                        new
                                            
                                                Priority = priority,
                                                Count = realbugs.Count()
                                            )
                );
    foreach (var data in processedData)
    
        Console.WriteLine(data.Category);

        foreach (var element in data.Bugs)
            Console.WriteLine("  " + element.Priority + " = " + element.Count);
    

【讨论】:

@Nandu:你意识到这与我的第二个查询基本相同,只是采用 lambda 表达式形式,对吧?请注意,它的性能与 Adrian 指出的相同(因为它是相同的查询)。 比 Jon Skeet 多花 9 分钟来写答案的危险 ;)【参考方案4】:

你可以这样做

 var retList = (from dbc in db.Companies
                           where dbc.IsVerified && dbc.SellsPCBs && !dbc.IsDeleted && !dbc.IsSpam && dbc.IsApproved
                           select new
                           
                               name = dbc.CompanyName,
                               compID = dbc.CompanyID,
                               state = dbc.State,
                               city = dbc.City,
                               businessType = dbc.BusinessType
                           ).GroupBy(k => k.state).ToList();


            List<dynamic> finalList = new List<dynamic>();

            foreach (var item in retList)
            
                finalList.Add(item.GroupBy(i => i.city));
            

【讨论】:

以上是关于LINQ 的嵌套组的主要内容,如果未能解决你的问题,请参考以下文章

Linq包括在嵌套组中,通过查询

C# + EntityFramework:通过查询将多个组转换为嵌套 JSON

Linq 到嵌套列表

使用 linq 查询嵌套列表

如何通过 orderBy 对嵌套的 Linq 对象进行排序

嵌套列表 Linq 返回 1 个最终列表 [重复]