【问题标题】:Full join in Linq完全加入 Linq
【发布时间】:2020-06-26 12:22:10
【问题描述】:

我是编写 Linq 查询的新手,想编写如下查询。

需求简介:

我需要为完全连接到另一个具有扩展数据的表的用户获取不同组键的计数

TABLE - 1:                                         Table - 2: 
---------------                                    -------------

| Id | GrpKey | prdId | UserId|                    | Id | GrpKey | GrpName  | UserId
| 1  | 123455 | Test1 | 11111                      | 1  | 123455 | GroupOne | 1111
| 2  | 123455 | Test2 | 22222                      | 2  | 551234 | GroupTwo | 1111
| 3  | 123455 | Test3 | 22222                      | 3  | 233344 |GroupThree| 1111
| 4  | 551234 | Test4 | 11111                      | 4  | 278344 |GroupFour | 1111
| 5  | 551234 | Test5 | 11111
| 6  | DBNULL | Test4 | 11111
| 7  | DBNULL | Test5 | 11111

REQD. RESULT for UserId : 11111 
--------------------------------

GrpKey | GrpName    | Count(GrpKey)
DBNULL | DBNULL     |  2
551234 | GroupTwo   |  2
123455 | GroupOne   |  1
233344 | GroupThree |  0
278344 | GroupFour  |  0

Queries Tried:

**LEFT JOIN:**
                          from item in table1
                          join grp in table2 on item.GrpKey equals grp.GrpKey  into j1
                          from rt in j1.DefaultIfEmpty()
                          where item.UserId == "1111"
                          group rt by rt.GrpKey into g
                          select new Group
                          {
                                UserId = grp.userId
                                Count = j1.Count(),
                                Name = grp.GrpName,
                                Key = grp.GrpKey,
                          }).ToList();

**RIGHT JOIN:**
              from grp in table2
                          join item in table1 on grp.GrpKey equals item.GrpKey  into j1                          
                          where grp.UserId == "1111"
                          group grp by grp.GrpKey into g
                          select new Group
                          {
                                UserId = grp.userId
                                Count = j1.Count(),
                                Name = grp.GrpName,
                                Key = grp.GrpKey,
                          }).ToList();

result = LeftJoinResult.Union(RightJoinResult).ToList();

TriedQuery1 的问题:

使用上面的 LINQ 查询,我得到的结果集是:

GrpKey | GrpName    | Count(GrpKey)
DBNULL | DBNULL     |  2
551234 | GroupTwo   |  2
123455 | GroupOne   |  1
551234 | GroupTwo   |  1
233344 | GroupThree |  1
278344 | GroupFour  |  1

请帮助我如何将此左联接转换为完全联接或数据,如 Reqd 结果中的那样

提前致谢

【问题讨论】:

  • 先做左连接,再做右连接,然后取两者的并集
  • 这是实现它的唯一方法吗?当我尝试实现联合时,存在重复行
  • 不,联合不会产生重复
  • 这能回答你的问题吗? LINQ - Full Outer Join
  • 我尝试了正确的连接,但后来我在每一行中都算作一个

标签: c# .net entity-framework linq


【解决方案1】:

LINQ 不包括适当的全外连接运算符,它可以由左外连接和右反半连接组成,但 LINQ 也没有右反半连接。

不幸的是,EF 无法有效地翻译我的完整外部连接代码以及GroupBy,因此您必须在客户端完成大部分工作。

使用为IEnumerable 提供适当、正确的完全外连接的扩展方法,而不会占用过多的内存:

public static class IEnumerableExt {
    public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(
        this IEnumerable<TLeft> leftItems,
        IEnumerable<TRight> rightItems,
        Func<TLeft, TKey> leftKeySelector,
        Func<TRight, TKey> rightKeySelector,
        Func<TKey, TLeft, TRight, TResult> resultSelector) =>
            from left in leftItems
            join right in rightItems on leftKeySelector(left) equals rightKeySelector(right) into rightj
            from right in rightj.DefaultIfEmpty()
            select resultSelector(leftKeySelector(left), left, right);

    public static IEnumerable<TResult> RightAntiSemiJoin<TLeft, TRight, TKey, TResult>(
        this IEnumerable<TLeft> leftItems,
        IEnumerable<TRight> rightItems,
        Func<TLeft, TKey> leftKeySelector,
        Func<TRight, TKey> rightKeySelector,
        Func<TKey, TLeft, TRight, TResult> resultSelector) {

        var hashLK = leftItems.Select(l => leftKeySelector(l)).ToHashSet();
        return rightItems.Where(r => !hashLK.Contains(rightKeySelector(r))).Select(r => resultSelector(rightKeySelector(r), default(TLeft), r));
    }

    public static IEnumerable<TResult> FullOuterJoin<TLeft, TRight, TKey, TResult>(
        this IEnumerable<TLeft> leftItems,
        IEnumerable<TRight> rightItems,
        Func<TLeft, TKey> leftKeySelector,
        Func<TRight, TKey> rightKeySelector,
        Func<TKey, TLeft, TRight, TResult> resultSelector) where TLeft : class {

        return leftItems.LeftOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector).Concat(leftItems.RightAntiSemiJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector));
    }
}

你可以计算你的答案:

var userId = "11111";

var ans = t1.Where(u => u.UserId == userId)
            .GroupBy(u => u.GrpKey)
            .ToList()
            .FullOuterJoin(t2.Where(t => t.UserId == userId), tg => tg.Key, t => t.GrpKey, (grpKey, ug, t) => new { grpKey, ug, t })
            .Select(gut => new { GrpKey = gut.grpKey, GrpName = gut.t != null ? gut.t.GrpName : null, Count = gut.ug != null ? gut.ug.Count() : 0 })
            .OrderByDescending(r => r.Count)
            .ThenBy(r => r.GrpKey);

【讨论】:

    【解决方案2】:

    所以你有一个Table2Elements 的序列,其中每个Table2Element 至少有一个GrpName、一个GrpKey 和一个UserId。

    此外,您有一个Table1Elements 序列,其中每个Table1Element 也有一个GrpKey 和一个UserId。

    要求:对于每个 Table2Element,给我 GrpKey、GrpName 以及与 Table2Element 具有相同 [GrpKey, UserId] 值的 Table1Element 的数量。

    每当您有一系列项目 A 时,其中每个项目 A 都有另一个表中属于该项目 A 的一些项目 B,例如学校及其学生、作者及其书籍、客户及其订单,请考虑使用 Enumerable .GroupJoin,或者由于您使用的是实体框架Queryable.GroupJoin

    var result = dbContext.Table2          // GroupJoin the elements of Table2
    .GroupJoin(dbContext.Table1,           // with the elements of Table1
    table2Element => new                   // from every element of Table2 take properties ...
    {
        GrpKey = table2Element.GrpKey,
        UserId = table2Element.UserId,
    },
    table1Element => new                   // from every element of Table1 take properties ...
    {
        GrpKey = table1Element.GrpKey,
        UserId = table1Element.UserId,
    },
    
    // parameter resultSelector: take the element of Table2,
    // with all zero or more elements of Table1 with the same key
    // to make one new resulting element:
    (table2Element, table1ElementsWithSameKey) => new
    {
        GrpKey = table2Element.GrpKey,
        GrpName = table2Element.GrpName,
    
        Count = table1ElementsWithSameKey.Count(),
    });
    

    简单的祝你好运!

    【讨论】:

      猜你喜欢
      • 2011-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-14
      • 2015-04-07
      • 2011-07-26
      相关资源
      最近更新 更多