【问题标题】:How do I aggregate joins?如何聚合联接?
【发布时间】:2011-02-22 15:47:22
【问题描述】:

我有三个相关的表:

Employee(EmployeeId, EmployeeName)

Skill(SkillId, SkillName)

EmployeeSkill(EmployeSkillId, EmployeeId, SkillId)

EmployeSkillId 是一个身份。

数据库中的行如下:

员工表:

员工 ID |员工编号 |员工姓名 ---------- | -------------- | ------------ 1 | 10015 |约翰·多伊

技能表:

技能 ID |技能名称 -------- | --------- 1 | 。网 2 | SQL 3 | OOD 4 |领导

员工技能表:

员工技能 ID |员工 ID |技能 ID --------------- | ---------- | -------- 1 | 1 | 1 2 | 1 | 2 3 | 1 | 3 4 | 1 | 4

如果我的员工在EmployeSkill 上注册了三个技能,我希望能够得到如下结果:

John Doe, "Skill-1, Skill2, Skill-3"

也就是说,将该员工的技能名称连接成一个字符串。

我尝试了以下方法,但它不起作用。

var query = from emp in Employee.All()
            from es in emp.EmployeeSkills
            join sk in Skill.All() on es.SkillId equals sk.SkillId
            group sk by new {emp.EmployeeName} into g
            select new TestEntity
            {
                Name = g.Key.EmployeeName,
                Skills = g.Aggregate(new StringBuilder(),
                                     (sb, grp_row) => sb.Append(grp_row.SkillName))
                          .ToString()
            };

技能名称的汇总列表返回为空。我该怎么做?

【问题讨论】:

  • 我已经为你格式化了问题。应该让您了解如何按照自己的意愿处理未来的问题。
  • 感谢 Jeff M,我一定会学习如何做的 :)

标签: linq linq-to-objects aggregate


【解决方案1】:

听起来您可以将连接作为选择的一部分:

var query = from emp in Employee.All()
            select new TestEntity {
                Name = emp.EmployeeName,
                Skills = string.Join(", ", 
                       (from es in emp.EmployeeSkills
                        join sk in Skill.All() on es.SkillId equals sk.SkillId
                        select sk.SkillName)) };

现在将分别为每个单独执行连接,这不是非常有效。另一种选择是先构建从技能 ID 到技能名称的映射:

var skillMap = Skill.All().ToDictionary(sk => sk.SkillId,
                                        sk => sk.SkillName);

那么主查询很简单:

var query = from emp in Employee.All()
            select new TestEntity {
                Name = emp.EmployeeName,
                Skills = string.Join(", ",
                       emp.EmployeeSkills.Select(sk => skillMap[sk.SkillId]))};

最终,有很多方法可以给这只猫剥皮 - 例如,如果您想坚持原来的方法,那仍然是可行的。我会这样做:

var query = from emp in Employee.All()
            from es in emp.EmployeeSkills
            join sk in Skill.All() on es.SkillId equals sk.SkillId
            group sk.SkillName by emp into g
            select new TestEntity
            {
                Name = g.Key.EmployeeName,
                Skills = string.Join(", ", g)
            };

此时它与您的原始查询非常相似,当然只是使用string.Join 而不是Aggregate。如果所有这三种方法都返回一个空的技能列表,那么我怀疑你的数据有问题。我不清楚为什么您的第一个查询会“成功”但技能列表为空。

编辑:好的,这是一个简短(-ish)但完整的工作示例:

using System;
using System.Collections.Generic;
using System.Linq;

public class Employee
{
    public int EmployeeId { get; set; }
    public string EmployeeName { get; set; }

    public static List<Employee> All { get; set; }

    public IEnumerable<EmployeeSkill> EmployeeSkills
    {
        get 
        { 
            return EmployeeSkill.All
                           .Where(x => x.EmployeeId == EmployeeId);
        }
    }
}

public class Skill
{
    public string SkillName { get; set; }
    public int SkillId { get; set; }

    public static List<Skill> All { get; set; }
}

public class EmployeeSkill
{
    public int SkillId { get; set; }
    public int EmployeeId { get; set; }

    public static List<EmployeeSkill> All { get; set; }
}

class Test
{
    static void Main()
    {
        Skill.All = new List<Skill>
        {
            new Skill { SkillName = "C#", SkillId = 1},
            new Skill { SkillName = "Java", SkillId = 2},
            new Skill { SkillName = "C++", SkillId = 3},
        };

        Employee.All = new List<Employee>
        {
            new Employee { EmployeeName = "Fred", EmployeeId = 1 },
            new Employee { EmployeeName = "Ginger", EmployeeId = 2 },
        };

        EmployeeSkill.All = new List<EmployeeSkill>
        {
            new EmployeeSkill { SkillId = 1, EmployeeId = 1 },
            new EmployeeSkill { SkillId = 2, EmployeeId = 1 },
            new EmployeeSkill { SkillId = 2, EmployeeId = 2 },
            new EmployeeSkill { SkillId = 3, EmployeeId = 2 },
        };

        var query = from emp in Employee.All
            from es in emp.EmployeeSkills
            join sk in Skill.All on es.SkillId equals sk.SkillId
            group sk.SkillName by emp.EmployeeName into g
            select new
            {
                Name = g.Key,
                Skills = string.Join(", ", g)
            };

        foreach (var result in query)
        {
            Console.WriteLine(result);
        }
    }
}

结果:

{ Name = Fred, Skills = C#, Java }
{ Name = Ginger, Skills = Java, C++ }

【讨论】:

  • 嘿乔恩。谢谢你这么快回复。这很奇怪,但是这两个选项都将 Skills 属性返回为空白。而且我确定这不是数据,因为只需使用联接并为每个员工/技能返回一行就可以了。有什么想法可能导致聚合不起作用?
  • @silverCORE:这听起来很奇怪...如果您添加更改以使用类似“Skills = g.Count().ToString();”的内容你有正确数量的技能吗?如果所有方法都给出相同的奇怪结果,那听起来确实存在数据问题。如果你能给出一个简短但完整的例子,那真的很有帮助。我会尝试自己建立一个。
  • 让我粘贴我拥有 Jon 的数据,并尝试 g.Count 的事情。在您的最后一个示例中,显示“由 emp.EmployeeName 组合 sk.SkillName”的部分显然使尝试使用 g.Key.EmployeeName 变得不可行。至少我的智能感知没有接收到它。
  • @silverCORE:是的,应该是按 emp 分组,或者按 emp.EmployeeName 分组,然后只使用 g.Key。我已经编辑了查询,还包含了一个简短但完整的示例,该示例确实 起作用。我想知道您是否只是无法正确显示结果?我认为查询很好。
  • 乔恩,你就是那个男人。感谢您花时间帮助我。真的很感激。
猜你喜欢
  • 2014-03-24
  • 1970-01-01
  • 2017-04-19
  • 1970-01-01
  • 2010-10-08
  • 2012-07-01
  • 2016-06-27
  • 2015-06-16
  • 1970-01-01
相关资源
最近更新 更多