【问题标题】:Combine tables using row values as column LINQ C# SQL使用行值作为列 LINQ C# SQL 组合表
【发布时间】:2015-07-02 09:03:23
【问题描述】:

我有一个users 表:

Id  | Name   | Age
-------------------- 
1   | Steve  | 21
2   | Jack   | 17
3   | Alice  | 25
4   | Harry  | 14

我还有一个包含其他用户信息的表格:

UId | Key    | Value
---------------------- 
1   | Height | 70
2   | Height | 65
2   | Eyes   | Blue
4   | Height | 51
3   | Hair   | Brown
1   | Eyes   | Green

UId 列链接到users 表中的Id 列。如您所见,并非所有用户都有相同的附加信息。爱丽丝没有身高值,杰克是唯一有眼睛颜色值的人等等。

有没有办法使用C#LINQ 查询将这些数据动态组合到一个表中,以便结果如下所示:

Id  | Name   | Age | Height | Eyes  | Hair
------------------------------------------ 
1   | Steve  | 21  |   70   | Green |     
2   | Jack   | 17  |   65   | Blue  |       
3   | Alice  | 25  |        |       | Brown   
4   | Harry  | 14  |   51   |

如果用户没有该列的值,它可以保持为空/null。这是否需要某种数据透视?

【问题讨论】:

  • 附加的用户信息表是不变的,还是应该在不更改 LINQ 的情况下始终可以使用新键?
  • 你认为你在删除你的反对意见后隐藏了自己? Stackoverflow 已经收到有关您活动的任何详细信息的通知。这里不是玩耍的地方,而是学习和分享知识的地方。
  • 别担心,我不扮演侦探。已经向 Stackoverflow 提供了足够的信息,以便进行重新搜索并确定谁是谁。

标签: c# sql linq


【解决方案1】:

对于这种情况,您的用户信息字段是不变的:

 var result = users.GroupJoin(details,
            user => user.Id,
            detail => detail.Id,
            (user, detail) => new
            {
                user.Id,
                user.Name,
                user.Age,
                Height = detail.SingleOrDefault(x => x.Key == "Height").Value,
                Eyes = detail.SingleOrDefault(x => x.Key == "Eyes").Value,
                Hair = detail.SingleOrDefault(x => x.Key == "Hair").Value,
            });

【讨论】:

  • 感谢您的回答!虽然有一些答案产生了预期的结果,但我认为你的答案是最简单的。
  • 但如果可能有多个 UId/Key 值,请小心。那么你应该使用 FirstOrDefault 或 LastOrDefault。
  • 是的,这实际上是我的确切情况。多个 UId 是可能的,所以我使用了FirstOrDefault。再次感谢!
  • 是的,这个查询非常简单。但是如果 Key 值不存在,那么这将失败,空异常不会被处理,但其他答案会这样做。
  • 嗯.. 当我查看生成的 SQL 查询时,我看不到可能存在的问题。它也适用于我生成的测试表。感谢您提供更多信息。
【解决方案2】:

您可以使用GroupJoin 来实现,例如:

var users = new List<Tuple<int, string, int>> {
    Tuple.Create(1, "Steve", 21),
    Tuple.Create(2, "Jack", 17),
    Tuple.Create(3, "Alice", 25),
    Tuple.Create(4, "Harry", 14)
};
var userInfos = new List<Tuple<int, string, string>> {
    Tuple.Create(1, "Height", "70"),
    Tuple.Create(2, "Height", "65"),
    Tuple.Create(2, "Eyes", "Blue"),
    Tuple.Create(4, "Height", "51"),
    Tuple.Create(3, "Hair", "Brown"),
    Tuple.Create(1, "Eyes", "Green"),
};
var query = users.GroupJoin(userInfos,
    u => u.Item1,
    ui => ui.Item1,
    (u, infos) => new { User = u, Infos = infos });
var result = query.Select(qi => new
{
    Id = qi.User.Item1,
    Name = qi.User.Item2,
    Age = qi.User.Item3,
    Height = qi.Infos.Where(i => i.Item2 == "Height").Select(i => i.Item3).SingleOrDefault(),
    Eyes = qi.Infos.Where(i => i.Item2 == "Eyes").Select(i => i.Item3).SingleOrDefault(),
    Hair = qi.Infos.Where(i => i.Item2 == "Hair").Select(i => i.Item3).SingleOrDefault()
});

【讨论】:

  • 您能解释一下Item1Item2Item3 是什么吗?因为我注意到它们没有在任何地方声明。感谢您的回复!
  • @Reece Kenney 这些是Tuple 类的属性,用于创建usersuserInfos 的元素。例如,users 元素 Item1 指的是 Id,Item2 指的是名称,依此类推。
【解决方案3】:

首先,我使用 Feature 对用户详细信息数据进行了分组(我已将 Key 属性重命名为 Feature 以避免混淆)和 UId,然后我使用 group join 使用 into g 组合这两个结果。最后使用指定的特征检索结果。

var result = from user in users
             join detail in details.GroupBy(x => new { x.UId, x.Feature })
             on user.Id equals detail.Key.UId into g
             select new
        {
           Id = user.Id,
           Name = user.Name,
           Age = user.Age,
           Height = g.FirstOrDefault(z => z.Key.Feature == "Height") != null ? 
              g.First(z => z.Key.Feature == "Height").First().Value : String.Empty,
           Eyes = g.FirstOrDefault(z => z.Key.Feature == "Eyes") != null ? 
              g.First(z => z.Key.Feature == "Eyes").First().Value : String.Empty,
           Hair = g.FirstOrDefault(z => z.Key.Feature == "Hair") != null ? 
              g.First(z => z.Key.Feature == "Hair").First().Value : String.Empty,
        };

我得到以下输出:-

这里是完整的Working Fiddle.

【讨论】:

    【解决方案4】:

    试试这个

    var list = (from u in context.users
                            join ud in context.UserDetails on u.Id equals ud.UId
                            select new
                            {
                                u.Id,
                                u.Name,
                                u.Age,
                                ud.Key,
                                ud.Value
                            });
    
                var finallist = list.GroupBy(x => new { x.Id, x.Name,x.Age}).Select(x => new
                    {
                        x.Key.Id,
                        x.Key.Name,
                        x.Key.Age,
                        Height = x.Where(y => y.Key == "Height").Select(y => y.Value).FirstOrDefault(),
                        Eyes = x.Where(y => y.Key == "Eyes").Select(y => y.Value).FirstOrDefault(),
                        Hair = x.Where(y => y.Key == "Hair").Select(y => y.Value).FirstOrDefault()
                    }).ToList();
    

    【讨论】:

      【解决方案5】:

      试试这个查询

      var objlist=( form a in contex.user
                    join b in contex.UserDetails on a.id equals a.Uid into gj
                    from subpet in gj.DefaultIfEmpty()
                              select new { Id=a.id, Name=a.name, Age=a.age, Height =subpet.Height,Eyes=subpet.Eyes, Hair=subpet.Hair}).ToList();
      

      【讨论】:

      • 在第 2 行,它应该是 from b in 而不是 from b into,因为使用 into 时出现语法错误。我在join 部分也遇到了错误,上面写着invalid expression term 'join'。感谢您的回复!
      • 好的,现在检查。没有说谢谢给正确的查询和投票标记
      • 属性 subpet.Height、subpet.Eyes、subpet.Hair 是从哪里来的?此查询将不起作用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-13
      相关资源
      最近更新 更多