티스토리 뷰

프로그래밍

LINQ 조인 구문 사용하기

야라바 2016. 10. 27. 13:58


 ※ LINQ 연관글 모음

 

조인(JOIN) 구문은 데이터베이스에서 테이블 또는 릴레이션을 연결하여 새로운 테이블이나 릴레이션을 생성해 주는 구문입니다. 물론 LINQ에서는 테이블만이 아니라 일반적인 데이터 오브젝트의 결합도 수행할  수 있도록 확장되었습니다. 사실 DBMS 입장에서는 조인 구문만큼 DB 시스템을 괴롭히는 구문도 없을 것입니다. 문장은 간단하지만 그 결과를 얻기 위해서 DBMS가 처리해야 하는 일은 간단하지 않기 때문입니다. 반대로 조인 구문을 적절하게 사용하면 시스템 효율 향상과 함께 프로그래밍의 생산성도 높이는 결과를 가져올 수 있습니다.

DB에서 여러가지 조인 구문이 있지만 대표적인 것은 내부(Inner) 조인의 동일 조인(Equi-Join)과 외부(OUTER) 조인의 LEFT OUTER JOIN 입니다. 조인에 있어 유념해야 할 사항은 결합 대상인 오브젝트들을 어떤 기준으로 결합할 것인가와 일치하는 키가 없을 경우의 처리입니다. 데이터베이스의 경우 주키(Primary key)-외래키(Foreign key) 체계를 효율적인 조인에 활용할 수 있겠지만 조인 구문 자체에는 영향이 없습니다. 더구나 XML이나 내부 오브젝트까지 조인을 사용 할 수 있는 LINQ의 경우에는 키나 인덱스의 존재와 관계없이 결합 대상들을 묶을 수 있는 기준을 잘 선정해야 합니다.

Dictionary<string, string> user_names = new Dictionary<string, string>()
    { {"hong1", "홍길동"}, {"hongsun", "홍길순"}, {"parkms", "박문수"}, {"kimsg", "김삿갓" },
      {"hwang1", "황금복"}, {"hwangso", "황소"}, {"kimsan", "김산"}, {"seohee", "서희"}};
Dictionary<string, int> user_ages = new Dictionary<string, int>() 
    { { "hong1", 23 }, { "hongsun", 32 }, { "parkms", 54 }, { "kimsg", 63 },
      {"hwang1", 45 }, {"hwangso", 25}};

var qry = from un in user_names
          join ua in user_ages on un.Key equals ua.Key
          select new { UserID = un.Key, UserName = un.Value, UserAge = ua.Value };

int i = 0;
foreach (var item in qry)
{
    Console.WriteLine(" {0} : {1} {2} {3} ", ++i, item.UserID, item.UserName, item.UserAge);
}

위의 코드와 실행 결과는 내부(Inner) 조인의 동일 조인(Equi-Join)의 예제입니다. join ... in ... on ... equals ... 구문으로 두 오브젝트를 결합하고 있으며 결합 기준으로 "equals" 양쪽에 두 사전 오브젝트의 키 값을 비교하도록 했습니다. 결과를 보면 두 사전 오브젝트의 키값이 일치하는 것만 표시되는 것을 확인할 수 있습니다. user_ages에 키값이 존재하지 않는 "kimsan", "seohee"는 결합 결과에서 제외됩니다. 이렇게 키값이 일치하지 않아서 제외되는 것까지를 포함시키는 것이 외부 조인(OUTER JOIN)입니다.


var qry = from un in user_names
          join ua in user_ages on un.Key equals ua.Key into urst
          from ua in urst.DefaultIfEmpty()
          select new { UserID = un.Key, UserName = un.Value, 
                             UserAge = (ua.Key == null) ? 0 : ua.Value };

위의 코드와 실행 결과는 외부 조인을 수행한 것으로  join ... in ... on ... equals ... 구문 이후에 into AAA로 조인 결과를 받고 이어서 from ... in AAA.DefaultIfEmpty()를 부가해주면 외부 조인을 수행할 수 있습니다. 위의 구문은 외부 조인(OUTER JOIN)중에서도 LEFT OUTER JOIN으로 첫 from 절에서 기술한 user_names 기준으로 외부 결합을 수행했습니다. 데이터베이스에서도 외부 조인이 키값에 해당하는 상대 테이블의 내용이 없으면 NULL이 넘어오는데 LINQ에서도 null이 넘어오므로 널에 대한 대처를 하는 부분이 추가되었습니다. RIGHT OUTER JOIN은 LEFT OUTER JOIN에서 결합 순서를 바꾸면 됩니다.

public class Userinfo
{
    public string UserID { get; set; }
    public string UserName { get; set; }
    public string UserType { get; set; }
    public int Age { get; set; }
    public bool Sex { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        List<Userinfo> userinfo = new List<Userinfo>() {
            new Userinfo { UserID = "hong1", UserName = "홍길동", UserType = "vip", Age = 50, Sex = true},
            new Userinfo { UserID = "parkms", UserName = "박문수", UserType = "vip", Age = 54, Sex = true},
            new Userinfo { UserID = "hongsun", UserName = "홍길순", UserType = "vvip", Age = 43, Sex = false},
            new Userinfo { UserID = "kimsg", UserName = "김삿갓", UserType = "general", Age = 35, Sex = true},
            new Userinfo { UserID = "hwang1", UserName = "황금복", UserType = "general", Age = 29, Sex = false},
            new Userinfo { UserID = "hwangso", UserName = "황소", UserType = "vip", Age = 63, Sex = true},
            new Userinfo { UserID = "kimsan", UserName = "김산", UserType = "general", Age = 23, Sex = true},
            new Userinfo { UserID = "seohee", UserName = "서희", UserType = "vvip", Age = 22, Sex = false}};
        
        
        Dictionary<string, int> user_fee = new Dictionary<string, int>() 
            { { "vip", 1000 }, { "vvip", 0 }, { "general", 3000 } };

        var qry = from uf in user_fee
                  join ui in userinfo on uf.Key equals ui.UserType
                  select new { uf, ui };

        int i = 0;
        foreach (var item in qry)
        {
            Console.WriteLine(" {0} : {1} {2} {3} ", ++i, item.uf.Key, item.ui.UserName, item.uf.Value);
        }
    }
}

앞서 소개한 내부조인 예제는 두 결합 대상의 키가 각각의 오브젝트 내에서 모두 중복되지 않는(주키, Primary Key) 경우였지만 위의 예제에서 user_fee와 같이 공통 정보를 가지고 있는 오브젝트와 결합되는 경우에는 다른 한쪽에서는(userinfo) 해당 키값을 가지고 있는 레코드가 여러개 존재할 수 있습니다. 이런 경우 위의 실행 결과에서도 확인할 수 있듯이 키 역할을 하는 오브젝트 기준으로 묶음(Grouping)이 가능합니다.  

var qry = from uf in user_fee
          join ui in userinfo on uf.Key equals ui.UserType into ug
          select new { uf, ug };

int i = 0;
foreach (var item in qry)
{
    Console.WriteLine(" {0} : {1} {2} ", ++i, item.uf.Key, item.uf.Value);
    foreach (var grp in item.ug)
    {
        Console.WriteLine("               {0} {1} {2} {3} ", 
              grp.UserID, grp.UserName, grp.Age, (grp.Sex) ? "남" : "여");
    }
}

참조가 되는 키 오브젝트를 기준으로 참조를 받는 오브젝트를 그룹으로 묶는 그룹 조인(Group join)을 수행하려면 위의 코드와 같이 조인 결과를 받아서 select 구문에 조인 결과를 사용하면 됩니다.


var qry = from uf in user_fee
          from ui in userinfo 
          select new { uf, ui };

int i = 0;
foreach (var item in qry)
{
    Console.WriteLine(" {0} : {1} {2} {3} {4}", ++i, item.uf.Key, item.uf.Value, item.ui.UserID, item.ui.UserType);
}

위의 예제 코드는 두가지의 오브젝트를 조인 구문없이 결합시킨 것으로 두 결합 대상의 항목수가 M, N이라면 결과 항목수는 M*N이 됩니다. join 구문은 없지만 이런 조인을 크로스(Cross) 조인이라 합니다.

'프로그래밍' 카테고리의 다른 글

LINQ 특정 항목 추출하기  (0) 2016.11.08
LINQ 필터링 다루기  (0) 2016.11.04
LINQ 조인 구문 사용하기  (0) 2016.10.27
비주얼스튜디오로 코드 분석하기  (0) 2016.10.19
LINQ 사용법 - 그룹과 집합 연산  (0) 2016.10.12
LINQ 정렬 기능 사용하기  (0) 2016.10.07

댓글
댓글쓰기 폼