复杂的 Linq to SQL 查询

Complicated Linq to SQL Query

我正在努力弄清楚 Linq-to-SQL 语法对于特定查询是什么。我可以在 SQL 中轻松完成此操作,但我无法在 Linq 中完全获得正确的语法。

我在两个数据库表中有 parent 和 child 记录,由外键链接。我希望我的结果基于这些规则 return 行:

  1. Return 每个 parent 恰好 1 行 ,无论存在多少 children。
  2. Return null/zero 值,如果 child 不存在。
  3. Return 来自具有空条件的 child 的相关数据。如果存在多个空条件,则 return 只是 第一个 一个。
  4. Return 具有 non-null 条件的 child 条记录的计数。

我在 .NET Fiddle 中研究这个问题有一段时间了,但一直做不好。这是我目前所拥有的(忽略随机描述!):

    IEnumerable<Parent> parents = new Parent[] { 
        new Parent { ID = 1, Description = "Apple" },
        new Parent { ID = 2, Description = "Orange" },
        new Parent { ID = 3, Description = "Pear" },
        new Parent { ID = 4, Description = "Banana" } };

    IEnumerable<Child> children = new Child[] {
        new Child { ID = 1, ParentID = 2, Description = "Mercury", Condition = null },
        new Child { ID = 2, ParentID = 3, Description = "Venus", Condition = null },
        new Child { ID = 3, ParentID = 3, Description = "Earth", Condition = null },
        new Child { ID = 4, ParentID = 4, Description = "Mars", Condition = null },
        new Child { ID = 5, ParentID = 4, Description = "Saturn", Condition = "> 5" } };

    /// What goes here...?
    var query = from p in parents
                join c in children on p.ID equals c.ParentID into jc                    
                from subchildren in jc.DefaultIfEmpty()
                select new Item { 
                    ParentID = p.ID, 
                    Description = p.Description, 
                    PrimaryChildID = subchildren == null ? 0 : subchildren.ID, 
                    SubDescription = subchildren == null ? null : subchildren.Description,
                    ConditionalCount = 0};

    foreach (var item in query) 
        Console.WriteLine("{0} {1} {2} {3} {4}",
            item.ParentID, 
            item.PrimaryChildID,                              
            item.Description,                
            item.SubDescription, 
            item.ConditionalCount);

我从中得到的输出是:

1 0 Apple  0
2 1 Orange Mercury 0
3 2 Pear Venus 0
3 3 Pear Earth 0
4 4 Banana Mars 0
4 5 Banana Saturn 0

但我想要这个:

1 0 Apple  0
2 1 Orange Mercury 0
3 2 Pear Venus 0
4 4 Banana Mars 1

任何人都可以帮助我了解此查询的正确语法吗?

这应该可以做到

    var query = (from p in parents
                     select new
                     {
                         ParentID = p.ID,
                         Description = p.Description,

                         PrimaryChildID = children.Where(c => c.ParentID == p.ID && c.Condition == null).Count() == 0 ? 0 : children.OrderBy(c=>c.ID).FirstOrDefault(c => c.ParentID == p.ID && c.Condition == null).ID,
                         SubDescription = children.Where(c => c.ParentID == p.ID && c.Condition == null).Count() == 0 ? null : children.OrderBy(c => c.ID).FirstOrDefault(c => c.ParentID == p.ID && c.Condition == null).Description,
                         ConditionalCount = children.Where(c => c.ParentID == p.ID && c.Condition != null).Count()
                     }).ToList();

您的情况不需要 left join,您需要 group join

根据MSDN:-

The group join is useful for producing hierarchical data structures. 
It pairs each element from the first collection with a set of correlated elements
from the second collection.

这样做:-

var query = from p in parents
       join c in children
       on p.ID equals c.ParentID into g
       let firstNullElement = g.FirstOrDefault(x => x.Condition == null)
       select new
      {
         ParentID = p.ID,
         PrimaryChildID = firstNullElement != null ? firstNullElement.ID : 0,
         Description = p.Description,
         SubDescription = firstNullElement!= null ? firstNullElement.Description
                                                  : String.Empty,
         ConditionalCount = g.Count(x => x.Condition != null)
      };

为了正确解释,这里是在我们使用 select new { } 投影我们实际需要的数据之前将生成的内容(证明 Group Join 的定义):-

ParentId                    g
----------------------------------------------
1                          null

2          ID = 1, ParentID = 2, Description = "Mercury", Condition = null

3          ID = 2, ParentID = 3, Description = "Venus", Condition = null
           ID = 3, ParentID = 3, Description = "Earth", Condition = null

4          ID = 4, ParentID = 4, Description = "Mars", Condition = null
           ID = 5, ParentID = 4, Description = "Saturn", Condition = "> 5"

现在,由于 g 持有 IEnumerable 个子元素,我们可以应用过滤器、项目数据、计数或做任何我们想做的事情,就像我们在最后的声明中使用 select.而且,正如我们所见,没有任何数据来自不同的子元素。

这是完整的Working Fiddle

这是我的查询变体:

        var query = from p in parents
                    join c in children on p.ID equals c.ParentID into jc
                    from subchildren in jc.DefaultIfEmpty()
                    select new
                    {
                        Parent = p,
                        Subchildren = subchildren
                    } into itemData
                    group itemData by itemData.Parent into g
                    select new Item
                    {
                        ParentID = g.Key.ID,
                        Description = g.Key.Description,
                        PrimaryChildID = g.Select(_ => _.Subchildren == null ? 0 : _.Subchildren.ID).FirstOrDefault(),
                        SubDescription = g.Select(_ => _.Subchildren == null ? null : _.Subchildren.Description).FirstOrDefault(),
                        ConditionalCount = 0
                    };