INNER JOIN(내부 조인)은 양쪽 컬렉션에서 조건이 일치하는 요소만 결과 집합에 포함합니다. 일치하지 않는 요소는 결과에서 모두 제외됩니다.
반면 LEFT OUTER JOIN(왼쪽 외부 조인)은 일치하는 모든 요소에 더해, 왼쪽 컬렉션에 있는 일치하지 않는 요소까지 모두 결과 집합에 포함합니다.
간단한 예제를 통해 왼쪽 외부 조인의 구현 방법을 살펴보겠습니다. 아래에는 Department(부서) 클래스와 Employee(직원) 클래스가 정의되어 있습니다. 여기서 주목할 점은 직원 Mary에게는 부서가 배정되어 있지 않다는 것입니다. 내부 조인을 사용하면 Mary의 레코드는 결과 집합에서 제외되지만, 왼쪽 외부 조인을 사용하면 부서가 없는 상태로 결과에 포함됩니다.
예제 코드
static class Program{
static void Main(string[] args){
var result = Employee.GetAllEmployees()
.GroupJoin(Department.GetAllDepartments(),
e => e.DepartmentID,
d => d.ID,
(emp, depts) => new { emp, depts })
.SelectMany(z => z.depts.DefaultIfEmpty(),
(a, b) => new{
EmployeeName = a.emp.Name,
DepartmentName = b == null ? "No Department" : b.Name
});
foreach (var v in result){
Console.WriteLine(" " + v.EmployeeName + "\t" + v.DepartmentName);
}
}
}
public class Department{
public int ID { get; set; }
public string Name { get; set; }
public static List<Department> GetAllDepartments(){
return new List<Department>(){
new Department { ID = 1, Name = "IT"},
new Department { ID = 2, Name = "HR"},
};
}
}
public class Employee{
public int ID { get; set; }
public string Name { get; set; }
public int DepartmentID { get; set; }
public static List<Employee> GetAllEmployees(){
return new List<Employee>(){
new Employee { ID = 1, Name = "Mark", DepartmentID = 1 },
new Employee { ID = 2, Name = "Steve", DepartmentID = 2 },
new Employee { ID = 3, Name = "Ben", DepartmentID = 1 },
new Employee { ID = 4, Name = "Philip", DepartmentID = 1 },
new Employee { ID = 5, Name = "Mary" }
};
}
}
실행 결과
Mark IT Steve HR Ben IT Philip IT Mary No Department
코드 동작 원리
위 예제에서 왼쪽 외부 조인은 세 가지 LINQ 확장 메서드의 조합으로 구현됩니다.
1. GroupJoin : 직원 컬렉션과 부서 컬렉션을 각각의 키(DepartmentID와 ID)를 기준으로 그룹화하여 연결합니다. 각 직원마다 해당하는 부서들의 시퀀스가 매핑됩니다.
2. DefaultIfEmpty : 특정 직원과 일치하는 부서가 하나도 없는 경우, 빈 시퀀스 대신 기본값(null)을 포함한 시퀀스를 반환합니다. 덕분에 Mary처럼 부서가 없는 직원도 결과에서 사라지지 않고 유지됩니다. 이것이 바로 왼쪽 외부 조인의 핵심입니다.
3. SelectMany : 그룹화된 시퀀스를 다시 평탄화(flatten)하여 최종 결과 형태로 변환합니다. 이때 부서 값이 null이면 "No Department" 문자열로 대체하여 출력합니다.
이처럼 LINQ의 확장 메서드인 GroupJoin, SelectMany, DefaultIfEmpty를 조합하면 SQL의 LEFT OUTER JOIN과 동일한 동작을 C# 코드로 손쉽게 구현할 수 있습니다.