LINQ:如何获得一组与相关表排序的表?

Posted

技术标签:

【中文标题】LINQ:如何获得一组与相关表排序的表?【英文标题】:LINQ: how to get a group of a table ordering with a related table? 【发布时间】:2013-02-07 12:14:14 【问题描述】:

我对我使用“分组依据”句子的 linq 产生的对象 IGrouping 有疑问。

我在数据库中有两个表,Products 和 Responses,它们的关系是 1 到 *。在 Responses 表中,我们有一个名为 FinalRate 的列,它是产品的费率。产品可以有 n 个响应或速率。

我想通过 FinalRate 的总和除以完成的费率数来获得 Products 订单。也就是说,按照平均分从高到低的顺序排列。

因为它可以在代码中读取(在问题的末尾),所以我尝试首先获得响应。总结所有的 finalrates 并将它们除以我使用一个组的计数。

代码有2个问题,即使当前代码有效:

1.-我尝试在单个查询中获取产品,但这是不可能的,因为我不能使用组中的产品表,然后使用“orderby”中的响应表。还有一件事 LINQ 只让您可以对一张表进行分组,不可能有“组刺激,响应”。 我在 LINQ 中找不到这个 sql 语句:

select prod.ProductID,prod.Commercial_Product_Name,prod.Manufacturer_Name,
prod.ProductImageUrl  
from rev_product prod  
inner join rev_response res on res.AtProductid=prod.ProductID 
group by prod.ProductID,prod.Commercial_Product_Name,prod.Manufacturer_Name
,prod.ProductImageUrl  
order by (sum(res.FinalRate)/count(res.AtProductid))

我试过了:

var gruposproductos = (from prod in ctx.Products 
                       join res in ctx.Responses on prod.ProductID equals res.AtProductId  
                       group  prod by prod.ProductID into g    
                       orderby (g.Sum(ra =>ra.FinalRate)/g.Count())
                       descending select g).Take(2);

但正如我所说,“orderby (g.Sum...”) 给出了错误,因为“into g”对 Product 表进行分组,而不是对 Response 表进行分组。

所以这就是为什么在我的最终代码中我没有在同一个 LINQ 语句中得到产品。

2.-一旦接受了这个事实,问题是我得到了一个 IGrouping,但是我没有得到一个响应列表,我可以在不执行代码中的两个 foreach 的情况下进行迭代。我只想要一个循环,就像你有一个“列表”对象一样。

这不是一个很酷的方法,但它确实有效。此外,我必须控制在第二个循环中只添加了 1 次。

有更好的代码吗?

var groupproducts = (from res in ctx.Responses    
                     group  res by res.AtProductId into g                                                 
                     orderby (g.Sum(ra =>ra.FinalRate)/g.Count()) 
                     descending select g).Take(2).ToList();

 List<Product> theproducts = new List<Product>();

 foreach (var groupresponse in groupproducts)
 

    foreach (var response in groupresponse)
    

          var producttemp= (from prod in ctx.Products
                            where prod.ProductID == response.AtProductId
                            select prod).First();

          theproducts.Add(producttemp);

        
     
  

最终解决方案(非常感谢@Daniel)

 var productsanonymtype = ctx.Products.Select(x => new
                
                    Product = x,                   
                    AverageRating = x.Responses.Count() == 0 ? 0 : x.Responses.Select(r => (double)r.FinalRate).Sum() / x.Responses.Count()
                ).OrderByDescending(x => x.AverageRating);

                List<Product> products = new List<Product>();

                foreach (var prod in productsanonymtype)
                
                    products.Add(prod.Product);
                

【问题讨论】:

【参考方案1】:

试试这个:

products.Select(x => new
                     
                         Product = x,
                         AverageRating = x.Responses.Sum(x => x.FinalRate) / 
                                         x.Responses.Count()
                     );

我使用的Sum 重载并未在所有提供程序中实现。如果这对您来说是个问题,您可以使用这个替代版本:

products.Select(x => new
                     
                         Product = x,
                         AverageRating = x.Responses.Select(x => x.FinalRate)
                                                    .Sum() / 
                                         x.Responses.Count()
                     );

如果没有从产品到其响应的导航属性,您应该首先尝试修复它。如果你不能,你可以使用这个版本:

products.Join(responses, x => x.Id, x => x.ProductId,
              (p, r) => new  Product = p, Response = r )
        .GroupBy(x => x.Product)
        .Select(g => new  Product = g.Key,
                           AverageRating = g.Select(x => x.Response.FinalRate)
                                            .Sum() / 
                                           g.Count()
                         );

假设 FinalRateint,这两种方法都将使用 int 计算平均评分,即不会有 4.5 评分。并且不会进行四舍五入,即 4.9 的实际平均评分将导致 4。您可以通过将除法的一个操作数强制转换为 double 来解决此问题。

另一个问题是到目前为止没有评级的情况。在这种情况下,上面的代码将导致异常。如果这对您来说是个问题,您可以将计算更改为:

AverageRating = g.Count() == 0
                ? 0
                : g.Select(x => (double)x.Response.FinalRate).Sum() / g.Count()

【讨论】:

令人印象深刻的答案 thx,令人难以置信的 LINQ 使用。我使用了第二种解决方案,我只需要添加排序部分: .OrderByDescending(x => x.AverageRating)【参考方案2】:
ctx.Products.GroupBy(x => new 
                                ProductId = x.ProductId, 
                                FinalRate = x.Responses.Sum(y => y.FinalRate),                  
                                CountProductId = x.Responses.Count
                              )
            .OrderBy(x => x.Key.FinalRate / x.Key.CountProductId);

这里有投影.....

ctx.Products.Select(x => new   
                                ProductID = x.ProductID, 
                                Commercial_Product_Name = x.Commercial_Product_Name, 
                                Manufacturer_Name = x.Manufacturer_Name,
                                ProductImageUrl = x.ProductImageUrl,
                                FinalRate = x.Responses.Sum(y => y.FinalRate), 
                                CountProductId = x.Responses.Count
                             )
            .GroupBy(x => new 
                               ProductId = x.ProductId, 
                               FinalRate = x.FinalRate,                  
                               CountProductId = x.CountProductId
                              )
            .OrderBy(x => x.Key.FinalRate / x.Key.CountProductId);

【讨论】:

非常感谢,我对您的答案投了赞成票,非常好,我不知道可以这样做。 @Daniel 之前回答过。他的方法的优点是您不会从零开始创建新对象,而是从查询中获取 Product 对象,这样您就可以用更少的代码来创建产品列表。

以上是关于LINQ:如何获得一组与相关表排序的表?的主要内容,如果未能解决你的问题,请参考以下文章

全国排名的问题(linq 的连表查询 等同于sql的left join)

如何在vue js中集成类似Excel的表排序和过滤

linq 多条件排序,该怎么处理

SparkSQL 中的表连接顺序以获得更好的性能

如何获得表的排列?

如何通过比较两个表来获得工作日数?