复杂的Linq到SQL查询

时间:2015-01-13 12:33:03

标签: c# linq group-by left-join

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

我在两个数据库表中有父记录和子记录,由外键链接。我希望我的结果根据这些规则返回行:

  1. 无论有多少孩子存在,每个父母都会返回 1行
  2. 如果孩子不存在,则返回null / zero值。
  3. 从具有空条件的子项返回相关数据。如果存在多个具有空条件的多个,则仅返回第一个一个。
  4. 返回具有非空条件的子记录数。
  5. 我已经在.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
    

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

3 个答案:

答案 0 :(得分:2)

在您的情况下,您不需要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

答案 1 :(得分:0)

这应该做的事情

    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();

答案 2 :(得分:0)

这是我的查询变体:

        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
                    };