LINQ 左连接、分组和计数生成错误结果

5

我在处理Linq(左联接 - 分组 - 计数)时遇到了困难,请帮助我。 以下是我的代码,它给出了这个结果。

Geography       2
Economy         1
Biology         1

I'm expecting this...

Geography       2
Economy         1
Biology         0

我该怎么解决这个问题?
class Department
{
    public int DNO { get; set; }
    public string DeptName { get; set; }
}

class Student
{
    public string Name { get; set; }
    public int DNO { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        List<Department> departments = new List<Department>
        {
            new Department {DNO=1, DeptName="Geography"},
            new Department {DNO=2, DeptName="Economy"},
            new Department {DNO=3, DeptName="Biology"}
        };

        List<Student> students = new List<Student>
        {
            new Student {Name="Peter", DNO=2},
            new Student {Name="Paul", DNO=1},
            new Student {Name="Mary", DNO=1},
        };

        var query = from dp in departments
                    join st in students on dp.DNO equals st.DNO into gst
                    from st2 in gst.DefaultIfEmpty()
                    group st2 by dp.DeptName into g
                    select new
                    {
                        DName = g.Key,
                        Count = g.Count()
                    };

        foreach (var st in query)
        {
            Console.WriteLine("{0} \t{1}", st.DName, st.Count);
        }
    }
}
4个回答

3
var query = 
            from department in departments
            join student in students on department.DNO equals student.DNO into gst
            select new
            {
                DepartmentName = department.DeptName,
                Count = gst.Count()
            };

我认为不需要任何分组来回答你的问题。你只想知道两件事: - 部门名称 - 每个部门的学生人数

通过使用'join'和'int o',您将联接结果放入临时标识符gst中。您只需要计算gst中结果的数量即可。


哇,这也是一个绝妙的想法。这是“Group Join”对吧?我知道在SQL术语中没有与之相当的概念。 但是,我尝试编写了一个类似于SQL的更加结构化的linq查询。感谢您的帮助~ SELECT D.DeptName, count(*) FROM Department D LEFT JOIN Student S ON D.DNO = S.DNO GROUP BY D.DeptName - C. Jun

2
var query = from dp in departments
            from st in students.Where(stud => stud.DNO == dp.DNO).DefaultIfEmpty()
            group st by dp.DeptName into g
            select new
            {
                DName = g.Key,
                Count = g.Count(x => x!=null)
            };

您想按系别名称对学生进行分组,但是要过滤掉空值学生的计数。我稍微更改了连接语法,尽管这并不太重要。 这里是一个可工作的演示

2

好的,看看@Danny在他的答案中说的内容,这是解决这个问题的最佳和最清洁的方法。顺便说一下,你也可以将其重写为lambda语法:

    var query = departments.GroupJoin(students,
                   dp => dp.DNO, st => st.DNO,
                   (dept,studs) => new
                       {
                           DName = dept.DNO,
                           Count = studs.Count()
                       });

我发现这种语法结果更加可预测,通常也更短。

顺便说一下:.GroupJoin 实际上是“左连接”,而 .Join 是“内连接”。要小心不要混淆它们。


应该是 new { dept.DeptName, studs.Count() }); - BurnsBA
1
请注意,这与 OP 的陈述显著不同:它只是 join - intogroup by 已经消失了。这就是为什么这个代码可以在没有像 Count(x => x!=null) 这样的东西的情况下工作。事实上,OP 将 GroupJoin 结果展开,引入了一个带有 null 的条目,然后再次对展平的列表进行分组。因此,这段代码不仅是 Igor 的重写,而且是一种改进。 - Gert Arnold
与此同时,丹尼发布了查询语法的替代方案。 - Gert Arnold
@GertArnold 如果将Danny查询转换为基于方法链的形式,代码将如下所示: var query = departments.GroupJoin(students, department => department.DNO, student => student.DNO, (department, gst) => new { DepartmentName = department.DeptName, Count = gst.Count() }); - BRAHIM Kamel
而且再次,对症治疗胜于解决问题的根本原因。我认为这个答案应该被接受。 - Gert Arnold
显示剩余4条评论

0

我的答案与@Igor类似

                    var query = from dp in departments
                    join st in students on dp.DNO equals st.DNO into gst
                    from st2 in gst.DefaultIfEmpty()
                    group st2 by dp.DeptName into g
                    select new
                    {
                        DName = g.Key,
                        Count = g.Count(std => std != null)
                    };

g.Count(std => std != null) 只是你需要做出的一个更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接