如何将Join与聚合函数和group by结合起来

时间:2018-04-25 00:00:58

标签: c# sql linq

我试图获得所有餐馆的平均评分,并返回与该ID相关的所有resteraunts的名称,我能够写一个sql语句来获得餐馆的平均值以及餐馆的名称但是我想要的只返回一次餐馆的​​名称。

Select  t.Average,  Name from [dbo].[Reviews] as rev
join [dbo].[Resteraunts] as rest
on rest.ResterauntId = rev.ResterauntId
inner join 

(
 SELECT [ResterauntId],

     Avg(Rating)     AS Average
   FROM [dbo].[Reviews]
   GROUP BY [ResterauntId]


)
as t on t.ResterauntId = rest.ResterauntId

resteraunt class

public int ResterauntId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }


public virtual ICollection<Review> Reviews { get; set; }

public virtual Review  reviews{ get; set; }

复习课程

public int ReviewId { get; set; }

        public double   Rating { get; set; }

        [ForeignKey("ResterauntId")]
        Resteraunt  Resteraunt { get; set; }
        public int ResterauntId { get; set; }
        public DateTime DateOfReview { get; set; }

如果可能的话,我希望将答案转换为linq。

3 个答案:

答案 0 :(得分:3)

Resteurants.Select(r => new {
    Average = r.Reviews.Average(rev => rev.Rating),
    r.Name
})

这应该会为您提供一组对象Average(该餐厅的所有评论的平均值)和餐馆的Name

这假设您已正确设置关系,以便Restaurant.Reviews仅引用按ID匹配的关系。

如果你没有设置这种关系,你需要自己过滤它:

Resteurants.Select(r => new {
    Average = Reviews.Where(rev => rev.ResteurantId == r.Id).Average(rev => rev.Rating),
    r.Name
})

答案 1 :(得分:2)

首先,你的模型似乎有比所需更多的聚合,我冒昧地修剪它并移除额外的字段,理想情况下你需要两个模型RestaurantId之间的关系,{{1的主键和Restaurant的外键(1:N)

Review

如果这些是模型,那么您只需要public class Restaurant { public int RestaurantId { get; set; } public string Name { get; set; } public string Address { get; set; } public string City { get; set; } public virtual ICollection<Review> Reviews { get; set; } } public class Review { public int ReviewId { get; set; } public double Rating { get; set; } public int RestaurantId { get; set; } public DateTime DateOfReview { get; set; } } ,因为内部包含审核集合,那么您需要的只是:

List<Restaurant> restaurantList

如果没有集合聚合,并且您有单独的ReviewList,如下所示:var result = restaurantList.Select(x => new { Name = x.Name, Average = x.Reviews.Average(y => y.Rating) } ); ,请执行以下操作:

List<Review> reviewList

另请注意,由于我们使用的是var result = reviewList.GroupBy(x => x.RestaurantId, x => new {x.RestaurantId,x.Rating}) .Join(restaurantList, x => x.Key,y => y.RestaurantId,(x,y) => new { Name = y.Name, AvgRating = x.Average(s => s.Rating) }); ,因此仅会列出至少有一条评论的餐馆,否则您需要InnerJoin使用LeftOuterJoin,评价为0的餐厅< / p>

答案 2 :(得分:2)

我看到您的Restaurant课程已经有ICollection<Review>代表餐馆的评论。这可能是因为您使用实体框架或类似的东西。

拥有这样的集合使得不必使用连接:

var averageRestaurantReview = Restaurants
    .Select(restaurant => new
    .Where(restaurant => ....) // only if you don't want all restaurants
    {
        Name = restaurant.Name,
        AverageReview = restaurants.Reviews
            .Select(review => review.Rating)
            .Average(),
    });

实体框架将为您进行适当的连接。

如果您真的想要使用某些内容,那么您需要Enumerable.GroupJoin

var averageRestaurantReview = Restaurants
    .GroupJoin(Reviews,                   // GroupJoin Restaurants and Reviews
        restaurant => restaurant.Id,      // From every restaurant take the Id
        review => review.RestaurantId,    // From every Review take the RestaurantId
    .Select( (restaurant, reviews) => new // take the restaurant with all matching reviews
    .Where(restaurant => ....)            // only if you don't want all restaurants
    {                                     // the rest is as before
        Name = restaurant.Name,
        AverageReview = reviews
            .Select(review => review.Rating)
            .Average(),
    });