Как объединить Join с агрегатной функцией и группировать по - PullRequest
0 голосов
/ 25 апреля 2018

Я пытаюсь получить средний рейтинг всех ресторанов и вернуть имена всех ресторанов, связанных с этим идентификатором. Я смог написать 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

класс ресторана

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 голосов
/ 25 апреля 2018

Во-первых, кажется, что ваши модели имеют больше агрегации, чем требовалось, я позволил себе обрезать его и убрать лишние поля, в идеале все, что вам нужно для связи между двумя моделями RestaurantId (Первичный ключ для 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, поскольку это внутренне содержит коллекцию рецензий, тогда всевам нужно:

var result = 
    restaurantList.Select(x => new {
                                    Name = x.Name,
                                    Average = x.Reviews.Average(y => y.Rating)
                                   }
                         );

В случае, если агрегации коллекции нет, и у вас есть отдельный ReviewList следующим образом: 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, используя GroupJoin, для ресторанов с 0 рейтингом

0 голосов
/ 25 апреля 2018

Я вижу, что у вашего Restaurant класса уже есть ICollection<Review>, который представляет отзывы о ресторане. Вероятно, это стало возможным, потому что вы используете Entity Framework или что-то подобное.

Наличие такой коллекции делает ненужным использование объединения:

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(),
    });

Entity Framework сделает правильные объединения для вас.

Если вы действительно хотите использовать что-то похожее на соединение, вам понадобится 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(),
    });
0 голосов
/ 25 апреля 2018
Resteurants.Select(r => new {
    Average = r.Reviews.Average(rev => rev.Rating),
    r.Name
})

Это должно дать вам набор объектов, которые имеют Average (среднее значение всех отзывов для этого ресторана) и Name ресторана.

Предполагается, что вы правильно настроили отношения, так что Restaurant.Reviews относится только к тем, которые совпадают по идентификатору.

Если у вас нет этой настройки отношений, и вам нужно отфильтровать ее самостоятельно:

Resteurants.Select(r => new {
    Average = Reviews.Where(rev => rev.ResteurantId == r.Id).Average(rev => rev.Rating),
    r.Name
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...