【问题标题】:Simple linq query using group in order to return a one to many relationship使用组的简单 linq 查询以返回一对多关系
【发布时间】:2012-08-27 05:26:03
【问题描述】:

我有一个用户数据表和另一个 user_ratings 表。这是一对多的关系,因此它们由UserId 连接,users 表是主键,user_ratings 具有外键关系。每个 user_ratings 都有一个名为 rating_value 的列,它用整数填充,负数或正数,并将其加在一起计算为用户的评分。我希望能够根据给定评级的日期为每个用户提取评级。这是我到目前为止的 linq 语句,但我认为它不正确。

var profiles = from userprofs in Ent.UserProfiles
               join userratings in Ent.UserRatings on userprofs.UserId equals userratings.UserId
               where userratings.DateAdded >= drm.Start
               group userprofs by userprofs.UserId into g
               select new { ... };

不太确定从这里去哪里,我不确定我是否正确使用了该组。我希望以后能够遍历这个集合,以便能够根据 user_ratings 的总和显示每个用户及其相关的评分

【问题讨论】:

    标签: c# asp.net linq


    【解决方案1】:

    如果您希望了解的有关评分的所有信息都是汇总信息(总和、平均值、计数等),那么您可以执行以下操作:

    var profiles = from userprof in Ent.UserProfiles
      join userrating in Ent.UserRatings on userprofs.UserId equals userratings.UserId
      where userrating.DateAdded >= drm.Start
      group userrating by userprof into g
      select new {g.Key.UserID, g.Key.UserName, Count = g.Count(), Avg = g.Avg(r => r.RatingValue) };
    

    请注意,我将复数名称更改为单数名称,因为我们根据每个单独的项目定义 linq 查询(UserProfiles 是复数,而 userprof 每个项目都在其中)。

    这可以很好地直接转换为 SQL:

    SELECT up.userID, up.userName, COUNT(ur.*), AVG(ur.ratingValue)
    FROM UserProfiles up JOIN UserRatings ur
    ON up.userID = ur.userID
    WHERE dateAdded > {whatever drm.Start is}
    GROUP BY up.userID, up.userName
    

    如果我们希望能够获得单独的评级,这与单个 SQL 查询不太匹配,我们可以这样做:

    var profiles = from userprof in Ent.UserProfiles
      join userrating in Ent.UserRatings on userprofs.UserId equals userratings.UserId
      where userrating.DateAdded >= drm.Start
      group new{userrating.RatingValue, userrating.SomeThing} by new{userprof.UserID, userprof.UserName};
    

    这将给我们一个IEnumerable<Grouping<{anonymous object}, {anonymous object}>>,我们可以迭代它,在每次迭代中获得一个新的Key.UserIDKey.UserName,并且能够依次迭代得到item.RatingValueitem.SomeThing(即我添加了用于演示,如果我们只想要这个值,它将是 IEnumerable<Grouping<{anonymous object}, int>>),如下所示:

    foreach(var group in profiles)
    {
      Console.WriteLine("The user is :" + group.Key.UserName + " (id: " + group.Key.UserID ")");
      foreach(var item in group)
      {
        Console.WriteLine(item.SomeThing + " was rated " + item.RatingValue);
      }
    }
    

    然而,这个问题是没有一个很好的单一 SQL 查询可以映射到。 Linq 会尽力而为,但这意味着要执行多个查询,所以最好还是帮忙:

    var profiles = from item in (from userprof in Ent.UserProfiles
      join userrating in Ent.UserRatings on userprofs.UserId equals userratings.UserId
      where userrating.DateAdded >= drm.Start
      select new{userrating.RatingValue, userrating.SomeThing, userprof.UserID, userprof.UserName}).AsEnumerable()
      group new{item.RatingValue, item.SomeThing} by new{item.UserID, item.UserName}
    

    这与以前的输出相同,但转换为 SQL 允许进行单个查询,其余工作在内存中完成。 99% 的情况下,像这样将一些工作拖到内存中会降低效率,但因为之前没有可以映射到的单个 SQL 查询,所以这是一个例外。

    【讨论】:

      【解决方案2】:

      如果我理解你的问题:

      from userprofs in Ent.UserProfiles
      join userratings in Ent.UserRatings on userprofs.UserId equals userratings.UserId
      where userratings.DateAdded >= drm.Start
      group new{userprofs,userratings} by userprofs.UserId into g
      select new { userId=g.Key, userRating=g.Sum(i=>i.userratings.rating_value) };
      

      GroupBy 返回 IEnumerableIGroupingIGrouping 基本上是一个 IEnumerable 带有一个额外的 Key 属性,其中包含用于进行分组的属性。如果你Sum 分组的rating_value,你就在那里。

      【讨论】:

      • 非常接近,但 i.rating_value 应该来自 userratings 表,目前我尝试了你在这里的内容,但它只给了我 userprofs 中的列
      • 如果你创建了一个匿名对象,你可以把它带到选择中。查看我的编辑。
      • 我假设您需要从 UserProfiles 行中提取的不仅仅是 userId,否则连接是多余的。您的用户评级列中已经有一个 userId,
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-01
      • 2017-10-04
      • 1970-01-01
      相关资源
      最近更新 更多