Step-by-Step C# 3.0 New Feature(7) – Query Keywords (orderby, join)

C# 3.0 2007/09/17 23:41

Step-by-Step  C# 3.0 New Feature(7) –

Query Keywords (orderby, join)

 

l  Implicitly Typed Local Variables

l  Anonymous Types

l  Auto-Implemented Properties

l  Object and Collection Initializer

l  Partial Method

l  Extension Method

l  Query Keywords

l  Query Expression

l  Lamda Expression

 

이번은 Query Keywords의 마지막 소개 글 되겠습니다. 아웅~ 좀 길게 달렸었죠? 그 만큼 많은 것들이 추가되었고, 또 알아야 할게 많아졌다는 의미네요. 그나마 다행인 것은, 대부분의 Keyword 들이 이미 SQL문에서 사용되고 있던 것들과 동일하고, 또 그 의미 또한 같아서, 코드 사용법만 익히면, 문제없이 사용 할 수 있다는 것입니다.

 

u  from clause

u  where clause

u  select clause

u  group clause

u  into clause

u  orderby clause

u  join clause

u  let clause

 

Orderby Clause

Orderby 절입니다. 역시 SQL과 같아서, 2개만 알고 있으면 되죠? Ascending Descending 이요. SQL에서는 DESC를 명시하지 않으면 기본적으로 ORDER BY 절은 Ascending입니다. 마찬가지로 Query 표현식에서도 orderby descending을 명시하지 않으면 기본적으로 Ascending으로 정렬이 됩니다.

string[] words2 = { "cherry", "apple", "blueberry" };

 

IEnumerable<string> sortedWords2 =

            from w in words2

            orderby w descending

            select w;

[코드 1 orderby ]

 

orderby 절에 대해서는, 더 이상 설명 드릴 것 이 없습니다. 냐햐햐햐  사실 이렇게 간단한 것을 소개 글에 포함시킨 건, 아마 구색 맞추기 일지도.. (^^;;)  모르겠네요..

 

Join Clause

이번엔, join 절 입니다. ~ 이 부분에 대해서는 앞 절보다 훨씬 더 할 이야기가 많습니다. SQL에서도 이 join절은 나름 고민해야 할 부분입니다. Join 처리 할 대상 테이블과의 join point를 잘 고려해야 하고, 또 조회되는 데이터의 결과를 어떻게 처리하느냐에 따라, 여러 가지 join 절을 적절하게 사용해야 하기 때문입니다.

C# 3.0에서 제공하는 join의 종류는 3가지가 있습니다.

 

Ø  Inner join

Ø  Group join

Ø  Outer join

 

Inner join은 아시는 바와 같이 join 하려는 두 테이블간의 공통 분모만을 조회합니다.

List<Owner> owners = new List<Owner> {

                    new Owner { Name="Hedlund, Magnus"  },

                    new Owner { Name="Adams, Terry" },

                    new Owner { Name="Weiss, Charlotte" },

                    new Owner { Name="Huff, Arlene" }

            };

 

            List<Pet> pets = new List<Pet> {

                    new Pet { Name="Barley", OwnerName="Adams, Terry" },

                    new Pet { Name="Boots", OwnerName="Adams, Terry" },

                    new Pet { Name="Whiskers", OwnerName="Weiss, Charlotte" },

                    new Pet { Name="Blue Moon", OwnerName="Raposo, Rui" },

                    new Pet { Name="Daisy", OwnerName="Hedlund, Magnus" }

            };

 

            var query = owners.Join(pets,

                            owner => owner.Name,

                            pet => pet.OwnerName,

                            (o, p) => new { OwnerName = o.Name,

                                                  PetName = p.Name });

 

            foreach (var ownerAndPets in query)

            {

                Console.WriteLine("{0} has cat \"{1}\"",

                    ownerAndPets.OwnerName,

                    ownerAndPets.PetName);

            }

[코드 2 inner join 예제 코드]

 

Owners pets 이 두 데이터 집합 간의 inner join은 조건 절이 지정하는 공통분모의 값만 추출됩니다. 그리고 그 기준이 되는 데이터 집합, 즉 위에서 owners Join 메소드를 호출함으로써, join 연산이 처리되는데, 이 메소드의 인자를 잘 보셔야 합니다.


사용자 삽입 이미지
[그림 1 join 메소드 인자 설명]

 

join 메소드의 인자는 위와 같습니다. 대상 데이터 집합을 넣고, 다음으로 기준 데이터 집합과 대상 데이터 집합의 join 조건을 각각 지정합니다. 그리고 이렇게 지정된 각 집합의 참조를 할당하면서, 조회 할 데이터 결과를 지정합니다. 위의 예제에서는 owner Class Name Pet Class Name 2개의 속성을 같은 Anonymous Type Class를 조회 결과의 형태로 지정했습니다. 이렇게 처리된 코드의 결과는 아래와 같습니다.


사용자 삽입 이미지
[그림 2 join 예제 코드 결과]

 

두 데이터 집합의 공통분모에 해당하는 데이터를 Name 속성만 추출한 형태로 출력한 결과입니다. Join 메소드로 구현된 이 코드는 Query Expression 식을 이용해 표현하면 아래와 같이 사용 할 수 있습니다.

var query = from o in owners

             join p in pets on o.Name equals p.OwnerName

             select new { OwnerName = o.Name,

PetName = p.Name };

[코드 3 Join 메소드를 Query Expression 식으로 표현]

 

이제는 Group Join을 알아볼 차례입니다. 조금 생소하죠? Group Join이라~ 그런데, 용어만 조금 생소할 뿐이지 의미나 기능은 전혀 생소하지 않습니다. 왜냐하면, Inner Join과 거의 동일하기 때문입니다. 1:N의 관계를 갖는 데이터 집합이 있다고 생각해 봅시다. 그러면 Inner Join으로  두 데이터 집합을 처리한다면, 분명히 기준 테이블 1건에 해당하는 대상 테이블의 데이터가 N개 존재하므로, 조회 결과는 기준 테이블 데이터에 해당하는 대상 테이블 개수만큼의 N개의 행이 나타날 것입니다. 이 의미가 곧 Inner Join 이면서 Group Join입니다. Group Join은 기준 테이블을 하나의 Group으로 묶어서, 대상테이블의 데이터를 조회한다 라는 개념으로 사용되고 있습니다. 그러니 이것은 Inner Join과 동일하다고 하겠습니다.

하지만, 이러한 기능은 이미 Inner Join에서 처리하는데, 굳이 왜 Group Join을 만들었을까요? ~ 그 정확한 이유는 저도 잘 모릅니다. 하지만 제 생각에는 Left Join을 처리하기 위해서 만들지 않았을까? 라는 추측은 할 수 있습니다. SQL문에서의 Left Join은 역시 그리 어렵지 않습니다. Join Keyword 하나만 바꾸면 되니까요~. 하지만 C# 3.0에서 Left Join은 조금 다릅니다. 아래 코드를 보시죠

var query1 = allOwners.GroupJoin(

                allPets,

                owner => owner.Name,

                pet => pet.OwnerName,

                (owner, pets) =>

                        pets.DefaultIfEmpty().Select(

                                 pet => new { OwnerName = owner.Name,

                                                    PetName =

                                                    (pet == null ? "":pet.Name)

                                                  }

                                                            )

                ).SelectMany(anon => anon);

[코드 4 Left Join 예제 코드]

 

~ 확실히 Inner Join절 보다 더 코드도 늘어났고, 보기에도 뭔가 복잡해 보입니다. 하지만, Inner Join 절에서 사용했던, 공통된 인자를 제외하고 본다면 그리 복잡해 보이지도 않습니다.

먼저, 조인 처리 할 대상 데이터 집합을 인자로 할당하고, 다음으로 조인 조건을 각각 기준 데이터 집합과 대상 데이터 집합에 대해서 지정합니다. 그리고 다음으로 오는 조건을 보셔야 하는데요, (owner, pets) 이 부분은 분명히 1:N의 집합입니다.

(owner, pets)

owner : 기준 데이터 집합 Owners를 구성하는 요소 클래스 변수

pets : 대상 데이터 집합 Pets 변수

이 변수들의 타입을 지정하지 않은 이유는, 이전에 설명 드렸던 것처럼 컴파일러가 구문을 분석 후 이 변수들의 타입을 유추해서 지정하기 때문입니다. 이런 똑똑한 컴파일로 덕을 앞으로 우리는 분명 많이 보겠지만, 처음에는 적응하지 못하는 좌절을 겪어야 하는 것은 어쩔 수 없는 것 같습니다. -_-;

다음으로 봐야 하는게, pets.DefaultIfEmpty() 메소드입니다. 이 코드는 메소드의 명명만 봐도 대략 짐작할 수 있을 것 같은데요, Left Join을 생각해 보면 1:N의 관계를 갖으며, 이러한 논리가 코드에 그대로 적용되어 있습니다.

하나의 owner N개의 pets를 대상으로 목록을 조회하는데, 만약 1개의 owner에 해당하는 데이터가 pets 중에 없으면, 그때는 기본값을 뿌려줘라!라는 의미의 명령이며, 여기서의 기본값은 null을 말합니다.

그렇기 때문에, 다음 코드에서 3항 연산자를 사용해서, N의 집합 pets 중 하나의 요소 pet null이면 빈 문자열을 출력하는 코드를 추가했습니다. 만약 이 3항 연산자가 없다면, null인 값의 Name 속성을 조회하려 했기 때문에 분명 NullReferenceException이 발생합니다.


사용자 삽입 이미지
[그림 3 Left Join 예제 코드 결과]

 

위 그림 3 Left Join의 결과입니다. Owner에 해당하지 않는 데이터가 빈 문자열 값으로 조회된 걸 확인 할 수 있습니다.

 

여기까지가 join 절에 대한 설명이었습니다. join 절에 대한 지금까지의 의미나 코드 활용이야 SQL문과 상당히 유사하기 때문에, 쉽게 이해 할 수 있지만! 한가지 걸리는 게 있죠? 바로 Lamda Expression입니다.

위에서 언급을 안 했지만, 위 코드는 상당히 많은 Lamda 표현식이 들어가 있습니다. 바로 =>연산자를 사용하는 부분이 모두 Lamda 표현식 인데요, 이 부분은 앞으로 소개 할 내용에 있기 때문에, 조금만 기다려 주시기 바랍니다. LINQ로 가기위해 꼭 거쳐야 하는 단계이고, 상당히 논리적이고, Codeless한 성격으로 함축적인 면이 강하기 때문에, 처음부터 확실히 이해하고 계셔야 할 것 같네요. 그럼 저는 여기서 마치겠습니다. 감사합니다. ^o^v

이올린에 북마크하기(0) 이올린에 추천하기(0)
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

Trackback Address :: http://zmeun.tistory.com/trackback/65 관련글 쓰기

Write a comment

◀ PREV : [1] : ... [79] : [80] : [81] : [82] : [83] : [84] : [85] : [86] : [87] : ... [145] : NEXT ▶