[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; }
답변
에 명시된 바와 같이 :
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
는 그가 오른쪽에 일치하지 않는 에도 검색했습니다.
여기 참조입니다