[c#] LINQ에서 왼쪽 외부 가입

join-on-equals-into절 을 사용하지 않고 C # LINQ에서 개체에 대해 왼쪽 외부 조인을 수행하는 방법은 무엇입니까? where절로 그렇게 할 수있는 방법이 있습니까? 올바른 문제 : 내부 조인이 쉽고 이런 해결책이 있습니다.

List<JoinPair> innerFinal = (from l in lefts from r in rights where l.Key == r.Key
                             select new JoinPair { LeftId = l.Id, RightId = r.Id})

그러나 왼쪽 외부 조인에는 솔루션이 필요합니다. 광산은 이런 식이지만 작동하지 않습니다.

List< JoinPair> leftFinal = (from l in lefts from r in rights
                             select new JoinPair {
                                            LeftId = l.Id,
                                            RightId = ((l.Key==r.Key) ? r.Id : 0
                                        })

여기서 JoinPair는 클래스입니다.

public class JoinPair { long leftId; long rightId; }



답변

에 명시된 바와 같이 :

LINQ 샘플 101 개-왼쪽 외부 결합

var q =
    from c in categories
    join p in products on c.Category equals p.Category into ps
    from p in ps.DefaultIfEmpty()
    select new { Category = c, ProductName = p == null ? "(No products)" : p.ProductName };


답변

데이터베이스 기반 LINQ 공급자를 사용하면 다음과 같이 훨씬 더 읽기 쉬운 왼쪽 외부 조인을 작성할 수 있습니다.

from maintable in Repo.T_Whatever
from xxx in Repo.T_ANY_TABLE.Where(join condition).DefaultIfEmpty()

당신이 생략하면 DefaultIfEmpty() 내부 조인이됩니다.

수락 된 답변을 선택하십시오.

  from c in categories
    join p in products on c equals p.Category into ps
    from p in ps.DefaultIfEmpty()

이 구문은 매우 혼란스럽고 MULTIPLE 테이블을 조인하려는 경우 어떻게 작동하는지 명확하지 않습니다.

참고 행당을 도입하지 않는 한 (적절한) 데이터베이스 최적화 프로그램이 왼쪽 조인으로 완벽하게 변환 할 수있는 외부 적용 / 왼쪽 결합 측면과 동일
하다는 점에 유의해야합니다. from alias in Repo.whatever.Where(condition).DefaultIfEmpty()-값 (실제 외부 적용). Linq-Object를 사용하는 경우 DB 최적화 프로그램이 없기 때문에 Linq-2-Objects에서이 작업을 수행하지 마십시오.

자세한 예

var query2 = (
    from users in Repo.T_User
    from mappings in Repo.T_User_Group
         .Where(mapping => mapping.USRGRP_USR == users.USR_ID)
         .DefaultIfEmpty() // <== makes join left join
    from groups in Repo.T_Group
         .Where(gruppe => gruppe.GRP_ID == mappings.USRGRP_GRP)
         .DefaultIfEmpty() // <== makes join left join

    // where users.USR_Name.Contains(keyword)
    // || mappings.USRGRP_USR.Equals(666)  
    // || mappings.USRGRP_USR == 666 
    // || groups.Name.Contains(keyword)

    select new
    {
         UserId = users.USR_ID
        ,UserName = users.USR_User
        ,UserGroupId = groups.ID
        ,GroupName = groups.Name
    }

);


var xy = (query2).ToList();

LINQ 2 SQL과 함께 사용하면 다음과 같은 매우 읽기 쉬운 SQL 쿼리로 변환됩니다.

SELECT
     users.USR_ID AS UserId
    ,users.USR_User AS UserName
    ,groups.ID AS UserGroupId
    ,groups.Name AS GroupName
FROM T_User AS users

LEFT JOIN T_User_Group AS mappings
   ON mappings.USRGRP_USR = users.USR_ID

LEFT JOIN T_Group AS groups
    ON groups.GRP_ID == mappings.USRGRP_GRP

편집하다:

보다 복잡한 예 는 ” SQL Server 쿼리를 Linq 쿼리로 변환 “을 참조하십시오
.

또한 Linq-2-SQL 대신 Linq-2-Objects에서 수행하는 경우 LINQ to SQL이 작업을 조인하기 위해 올바르게 변환하기 때문에이 방법을 객체로 사용하기 때문에 구식 방식으로 수행해야합니다. 전체 검색을 강제 실행하고 색인 검색을 이용하지 않습니다.

    var query2 = (
    from users in Repo.T_Benutzer
    join mappings in Repo.T_Benutzer_Benutzergruppen on mappings.BEBG_BE equals users.BE_ID into tmpMapp
    join groups in Repo.T_Benutzergruppen on groups.ID equals mappings.BEBG_BG into tmpGroups
    from mappings in tmpMapp.DefaultIfEmpty()
    from groups in tmpGroups.DefaultIfEmpty()
    select new
    {
         UserId = users.BE_ID
        ,UserName = users.BE_User
        ,UserGroupId = mappings.BEBG_BG
        ,GroupName = groups.Name
    }

);


답변

람다 식 사용

db.Categories
  .GroupJoin(db.Products,
      Category => Category.CategoryId,
      Product => Product.CategoryId,
      (x, y) => new { Category = x, Products = y })
  .SelectMany(
      xy => xy.Products.DefaultIfEmpty(),
      (x, y) => new { Category = x.Category, Product = y })
  .Select(s => new
  {
      CategoryName = s.Category.Name,
      ProductName = s.Product.Name
  });


답변

이제 확장 방법으로 :

public static class LinqExt
{
    public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(this IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, TKey> leftKey, Func<TRight, TKey> rightKey,
        Func<TLeft, TRight, TResult> result)
    {
        return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r })
             .SelectMany(
                 o => o.r.DefaultIfEmpty(),
                 (l, r) => new { lft= l.l, rght = r })
             .Select(o => result.Invoke(o.lft, o.rght));
    }
}

일반적으로 join을 사용하는 것처럼 사용하십시오.

var contents = list.LeftOuterJoin(list2,
             l => l.country,
             r => r.name,
            (l, r) => new { count = l.Count(), l.country, l.reason, r.people })

이것이 시간을 절약하기를 바랍니다.


답변

예제를 살펴보십시오 . 이 쿼리는 작동해야합니다.

var leftFinal = from left in lefts
                join right in rights on left equals right.Left into leftRights
                from leftRight in leftRights.DefaultIfEmpty()
                select new { LeftId = left.Id, RightId = left.Key==leftRight.Key ? leftRight.Id : 0 };


답변

확장 메소드에 의한 왼쪽 외부 조인의 구현은 다음과 같습니다.

public static IEnumerable<Result> LeftJoin<TOuter, TInner, TKey, Result>(
  this IEnumerable<TOuter> outer, IEnumerable<TInner> inner
  , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
  , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    if (outer == null)
      throw new ArgumentException("outer");

    if (inner == null)
      throw new ArgumentException("inner");

    if (outerKeySelector == null)
      throw new ArgumentException("outerKeySelector");

    if (innerKeySelector == null)
      throw new ArgumentException("innerKeySelector");

    if (resultSelector == null)
      throw new ArgumentException("resultSelector");

    return LeftJoinImpl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer ?? EqualityComparer<TKey>.Default);
  }

  static IEnumerable<Result> LeftJoinImpl<TOuter, TInner, TKey, Result>(
      IEnumerable<TOuter> outer, IEnumerable<TInner> inner
      , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
      , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    var innerLookup = inner.ToLookup(innerKeySelector, comparer);

    foreach (var outerElment in outer)
    {
      var outerKey = outerKeySelector(outerElment);
      var innerElements = innerLookup[outerKey];

      if (innerElements.Any())
        foreach (var innerElement in innerElements)
          yield return resultSelector(outerElment, innerElement);
      else
        yield return resultSelector(outerElment, default(TInner));
     }
   }

그러면 결과 선택기가 널 요소를 처리해야합니다. Fx.

   static void Main(string[] args)
   {
     var inner = new[] { Tuple.Create(1, "1"), Tuple.Create(2, "2"), Tuple.Create(3, "3") };
     var outer = new[] { Tuple.Create(1, "11"), Tuple.Create(2, "22") };

     var res = outer.LeftJoin(inner, item => item.Item1, item => item.Item1, (it1, it2) =>
     new { Key = it1.Item1, V1 = it1.Item2, V2 = it2 != null ? it2.Item2 : default(string) });

     foreach (var item in res)
       Console.WriteLine(string.Format("{0}, {1}, {2}", item.Key, item.V1, item.V2));
   }


답변

이 예제를 보아라

class Person
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Phone { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

public static void LeftOuterJoinExample()
{
    Person magnus = new Person {ID = 1, FirstName = "Magnus", LastName = "Hedlund"};
    Person terry = new Person {ID = 2, FirstName = "Terry", LastName = "Adams"};
    Person charlotte = new Person {ID = 3, FirstName = "Charlotte", LastName = "Weiss"};
    Person arlene = new Person {ID = 4, FirstName = "Arlene", LastName = "Huff"};

    Pet barley = new Pet {Name = "Barley", Owner = terry};
    Pet boots = new Pet {Name = "Boots", Owner = terry};
    Pet whiskers = new Pet {Name = "Whiskers", Owner = charlotte};
    Pet bluemoon = new Pet {Name = "Blue Moon", Owner = terry};
    Pet daisy = new Pet {Name = "Daisy", Owner = magnus};

    // Create two lists.
    List<Person> people = new List<Person> {magnus, terry, charlotte, arlene};
    List<Pet> pets = new List<Pet> {barley, boots, whiskers, bluemoon, daisy};

    var query = from person in people
        where person.ID == 4
        join pet in pets on person equals pet.Owner  into personpets
        from petOrNull in personpets.DefaultIfEmpty()
        select new { Person=person, Pet = petOrNull};



    foreach (var v in query )
    {
        Console.WriteLine("{0,-15}{1}", v.Person.FirstName + ":", (v.Pet == null ? "Does not Exist" : v.Pet.Name));
    }
}

// This code produces the following output:
//
// Magnus:        Daisy
// Terry:         Barley
// Terry:         Boots
// Terry:         Blue Moon
// Charlotte:     Whiskers
// Arlene:

이제 당신은 include elements from the left그 요소라도 has no matches in the right우리의 경우에Arlene 는 그가 오른쪽에 일치하지 않는 에도 검색했습니다.

여기 참조입니다

방법 : 왼쪽 외부 조인 수행 (C # 프로그래밍 가이드)