LINQ: join(내부 조인 vs 외부 조인)

00·2024년 12월 29일

C#

목록 보기
90/149
using System;
using System.Linq;


/*
join:
두 데티어 원본을 연결하는 연산입니다.
각 데이터 원본에서 특정 필드의 값을 비교하여 일치하는 데이터끼리 연결합니다.

(1) Inner Join(내부 조인):
두 데이터 원본 사이에서 일치하는 데이터들만 연결한 후 반환합니다.(교집합과 비슷)
구체적으로, 첫 번째 데이터 원본과 두 번째 데이터 원본의 특정 필드를 비교해서 일치하는 데이터를 반환합니다.
이때 기준은 '첫 번째 데이터 원본'입니다.
기준 데이터 원본에는 존재하지만 연결할 데이터 원본에는 존재하지 않는 데이터는 조인 결과에 포함되지 않습니다.

내부 조인은 join 절을 통해 수행합니다.

fromm a in A
join b in B on a.XXXX equals b.YYYY

기준 데이터 a는 from 절에서 뽑아낸 범위 변수이고,
연결 대상 데이터 b는 join 절에서 뽑아낸 변수입니다.
on 키워드는 조인 조건을 수반합니다.
on 절의 조인 조건은 동등(Equality)만 허용됩니다. 즉, '~보다 큼' 같은 비교 연사나은 허락되지 않습니다.
이때 '==' 연산자가 아닌, 'equals' 키워드를 사용해야 합니다.

(2) Outer Join(외부 조인):
내부 조인과 달리, 조인 결과에 기준이 되는 데이터 원본이 모두 포함됩니다.
따라서 연결할 데이터 원본에 기준 데이터 원본의 데이터와 일치하는 데이터가 없다면, 
그 부분은 빈 값으로 결과를 채우게 됩니다.


LINQ는 원래 DBMS에서 사용하던 SQL을 본떠 프로그래밍 언어 안에 통합한 것입니다.
원래 SQL에서 지원하는 외부 조인에는 왼쪽, 오른쪽, 완전 외부 조인이 있으나,
LINQ는 왼쪽 조인만 지원합니다.
 */


// LINQ를 사용하여 두 개의 데이터 배열(Profile, Product)을 조인하는 방법
// 
namespace Join
{
    // (1)  클래스 정의
    class Profile
    {
        public string Name { get; set; }
        public int Height { get; set; }
    }

    class Product
    {
        public string Title { get; set; }
        public string Star { get; set; }
    }

    class MainApp
    {
        static void Main(string[] args)
        {
            // (2) 데이터 배열 생성
            Profile[] arrProfile = // 5개의 Profile 객체를 생성하고,
                                   // 중괄호 {} 안에 각 객체의 Name과 Height 프로퍼티 값을 초기화하고 있습니다. 
                                   // 이렇게 생성된 5개의 객체를 arrProfile 배열에 저장합니다.
            {
                new Profile(){Name="정우성", Height=186},
                new Profile(){Name="김태희", Height=158},
                new Profile(){Name="고현정", Height=172},
                new Profile(){Name="이문세", Height=178},
                new Profile(){Name="하하", Height=171}
            };

            Product[] arrProduct = // 5개의 Pro duct 객체를 생성하여
                                   // arrProduct 배열에 저장합니다.
            {
                new Product(){Title="비트",        Star="정우성"},
                new Product(){Title="CF 다수",     Star="김태희"},
                new Product(){Title="아이리스",    Star="김태희"},
                new Product(){Title="모래시계",    Star="고현정"},
                new Product(){Title="Solo 예찬",   Star="이문세"}
            };


            // (3) 내부 조인 (Inner Join)
            var listProfile =
                from profile in arrProfile // arrProfile 배열의 각 profile 요소와
                join product in arrProduct on profile.Name equals product.Star // arrProduct 배열의 각 product 요소를
                                                                               // profile.Name 프로퍼티 값과 product.Star 프로퍼티 값이 같은 경우에
                                                                               // 조인합니다.
                                                                               // 즉, 배우 이름으로 프로필 정보와 제품 정보를 연결합니다.
                select new // 조인된 결과에서 Name, Work, Height라는 세 개의 프로퍼티를 가진 익명 객체를 생성합니다.
                           // 이는 '조인 결과를 원하는 형태로 가공하여 새로운 객체를 생성하는 과정'입니다.
                {
                    Name = profile.Name, // 새로운 객체의 Name 프로퍼티에 profile.Name 값을 할당합니다.
                                         // 즉, arrProfile 배열에서 가져온 프로필의 이름을,
                                         // 새로운 객체의 Name 프로퍼티에 저장합니다.
                    Work = product.Title, // 새로운 객체의 Work 프로퍼티에 product.Title 값을 할당합니다.
                                          // 즉, arrProduct 배열에서 가져온 제품의 이름을,
                                          // 새로운 객체의 Work 프로퍼티에 저장합니다.
                    Height = profile.Height // 새로운 객체의 Height 프로퍼티에 profile.Height 값을 할당합니다.
                                            // 즉, arrProfile 배열에서 가져온 프로필의 키를, 
                                            // 새로운 객체의 Height 프로퍼티에 저장합니다.
                };

            Console.WriteLine("--- 내부 조인 결과 ---");
            foreach (var profile in listProfile) // 내부 조인 결과를 출력합니다. 
            {
                Console.WriteLine("이름:{0}, 작품:{1}, 키:{2}cm",
                    profile.Name, profile.Work, profile.Height);
            }


            // (4) 외부 조인 (Outer Join)
            listProfile =
                from profile in arrProfile // arrProfile 배열의 각 profile 요소와 
                join product in arrProduct on profile.Name equals product.Star into ps // arrProduct 배열의 각 product 요소를
                                                                                       // profile.Name 프로퍼티 값과 product.Star 프로퍼티 값이 같은 경우에
                                                                                       // 조인하고, 결과를 ps라는 그룹 변수에 저장합니다.
                from sub_product in ps.DefaultIfEmpty(new Product() { Title = "그런거 없음" }) // ps 그룹 변수에서 DefaultIfEmpty() 메서드를 사용하여
                                                                                          // 조인 결과가 없는 경우 (즉, 해당 배우가 출연한 작품이 없는 경우),
                                                                                          // "그런거 없음"이라는 제목을 가진 새로운 Product 객체를 생성합니다.
                select new // 조인된 결과에서 Name, Work, Height라는 세 개의 프로퍼티를 가진 익명 객체를 생성합니다.
                {
                    Name = profile.Name,
                    Work = sub_product.Title,
                    Height = profile.Height
                };


            Console.WriteLine();
            Console.WriteLine("--- 외부 조인 결과 ---");
            foreach (var profile in listProfile) // 외부 조인 결과를 출력합니다. 
            {
                Console.WriteLine("이름:{0}, 작품:{1}, 키:{2}cm",
                    profile.Name, profile.Work, profile.Height);
            }
        }
    }
}


/*
출력 결과

--- 내부 조인 결과 ---
이름:정우성, 작품:비트, 키:186cm
이름:김태희, 작품:CF 다수, 키:158cm
이름:김태희, 작품:아이리스, 키:158cm
이름:고현정, 작품:모래시계, 키:172cm
이름:이문세, 작품:Solo 예찬, 키:178cm

--- 외부 조인 결과 ---
이름:정우성, 작품:비트, 키:186cm
이름:김태희, 작품:CF 다수, 키:158cm
이름:김태희, 작품:아이리스, 키:158cm
이름:고현정, 작품:모래시계, 키:172cm
이름:이문세, 작품:Solo 예찬, 키:178cm
이름:하하, 작품:그런거 없음, 키:171cm

 */

코드 설명

코드 설명

이 코드는 C#에서 LINQ를 사용하여 두 개의 데이터 배열 (Profile, Product)을 조인하는 방법을 보여주는 예제입니다.

1. 클래스 정의

먼저, 두 개의 클래스 ProfileProduct를 정의합니다.

  • Profile 클래스:

    • Name 프로퍼티 (string 타입): 사람의 이름을 나타냅니다.
    • Height 프로퍼티 (int 타입): 사람의 키를 나타냅니다.
  • Product 클래스:

    • Title 프로퍼티 (string 타입): 제품의 이름을 나타냅니다.
    • Star 프로퍼티 (string 타입): 해당 제품에 출연한 배우의 이름을 나타냅니다.

2. 데이터 배열 생성

Main 메서드에서는 Profile 객체 배열 arrProfileProduct 객체 배열 arrProduct를 생성합니다. 각 배열은 샘플 데이터로 초기화됩니다.

  • arrProfile: 정우성, 김태희, 고현정, 이문세, 하하의 프로필 정보를 담고 있습니다. 각 프로필은 이름과 키 정보를 가지고 있습니다.
  • arrProduct: 비트, CF 다수, 아이리스, 모래시계, Solo 예찬의 제품 정보를 담고 있습니다. 각 제품은 제품명과 해당 제품에 출연한 배우 정보를 가지고 있습니다.

3. 내부 조인 (Inner Join)

내부 조인은 두 데이터 원본에서 조인 조건을 만족하는 데이터만 결합하는 방식입니다. 즉, arrProfile 배열의 프로필과 arrProduct 배열의 제품 중에서 배우 이름이 일치하는 경우에만 결과에 포함됩니다.

  • from profile in arrProfile join product in arrProduct on profile.Name equals product.Star: arrProfile 배열의 각 profile 요소와 arrProduct 배열의 각 product 요소를 profile.Name 프로퍼티 값과 product.Star 프로퍼티 값이 같은 경우에 조인합니다. 즉, 배우 이름으로 프로필 정보와 제품 정보를 연결합니다.
  • select new { Name = profile.Name, Work = product.Title, Height = profile.Height }: 조인된 결과에서 새로운 익명 객체를 생성하여 Name, Work, Height 프로퍼티를 선택합니다. 이는 조인 결과를 원하는 형태로 가공하는 과정입니다.
  • foreach 루프를 사용하여 내부 조인 결과를 출력합니다. 각 프로필의 이름, 출연 작품, 키를 출력합니다.

4. 외부 조인 (Outer Join)

외부 조인은 두 데이터 원본 중 하나의 데이터 원본에 있는 모든 데이터를 포함하고, 다른 데이터 원본에는 일치하는 데이터가 없더라도 결과에 포함하는 방식입니다. 이 코드에서는 왼쪽 외부 조인을 사용하며, arrProfile 배열의 모든 프로필 정보를 포함하고, arrProduct 배열에 일치하는 제품 정보가 없는 경우 "그런거 없음"이라는 값을 표시합니다.

  • from profile in arrProfile join product in arrProduct on profile.Name equals product.Star into ps: arrProfile 배열의 각 profile 요소와 arrProduct 배열의 각 product 요소를 profile.Name 프로퍼티 값과 product.Star 프로퍼티 값이 같은 경우에 조인하고, 결과를 ps라는 그룹 변수에 저장합니다.
  • from sub_product in ps.DefaultIfEmpty(new Product() { Title = "그런거 없음" }): ps 그룹 변수에서 DefaultIfEmpty 메서드를 사용하여 조인 결과가 없는 경우 (즉, 해당 배우가 출연한 작품이 없는 경우) "그런거 없음"이라는 제목을 가진 새로운 Product 객체를 생성합니다.
  • select new { Name = profile.Name, Work = sub_product.Title, Height = profile.Height }: 조인된 결과에서 새로운 익명 객체를 생성하여 Name, Work, Height 프로퍼티를 선택합니다.
  • foreach 루프를 사용하여 외부 조인 결과를 출력합니다. 각 프로필의 이름, 출연 작품, 키를 출력합니다.

요약

이 코드는 LINQ를 사용하여 내부 조인과 외부 조인을 수행하는 방법을 보여줍니다. 내부 조인은 두 데이터 원본에서 일치하는 항목만 반환하는 반면, 외부 조인은 왼쪽 데이터 원본의 모든 항목과 일치하는 오른쪽 데이터 원본의 항목을 반환합니다. 외부 조인은 일치하는 항목이 없는 경우 왼쪽 데이터 원본의 항목에 대해 null 값 대신 기본값을 제공합니다. 이 예제에서는 DefaultIfEmpty 메서드를 사용하여 null 값 대신 "그런거 없음"이라는 기본값을 제공합니다.

5개의 Profile 객체를 생성하여 arrProfile 배열에 저장합니다?

네, 맞는 설명입니다.

코드에서 arrProfileProfile 객체를 저장하는 배열입니다. 그리고 코드에서 5개의 Profile 객체를 생성하여 arrProfile 배열에 저장하고 있습니다.

좀 더 자세히 설명드리면,

Profile[] arrProfile =
{
    new Profile(){Name="정우성", Height=186},
    new Profile(){Name="김태희", Height=158},
    new Profile(){Name="고현정", Height=172},
    new Profile(){Name="이문세", Height=178},
    new Profile(){Name="하하", Height=171}
};

이 부분에서 new Profile(){...} 형태로 5개의 Profile 객체를 생성하고, 중괄호 {} 안에 각 객체의 NameHeight 프로퍼티 값을 초기화하고 있습니다. 그리고 이렇게 생성된 5개의 객체를 arrProfile 배열에 저장하는 것입니다.

따라서 "5개의 Profile 객체를 생성하여 arrProfile 객체 배열에 저장합니다."는 코드에 대한 정확한 설명입니다.

arrProfile 배열에는 어떤 것들이 들어있나?

arrProfile 배열에는 5개의 Profile 객체가 들어있습니다. 각 Profile 객체는 배우의 이름과 키 정보를 가지고 있습니다.

좀 더 자세히 설명드리면 다음과 같습니다.

  • arrProfile[0]:

    • Name: "정우성"
    • Height: 186
  • arrProfile[1]:

    • Name: "김태희"
    • Height: 158
  • arrProfile[2]:

    • Name: "고현정"
    • Height: 172
  • arrProfile[3]:

    • Name: "이문세"
    • Height: 178
  • arrProfile[4]:

    • Name: "하하"
    • Height: 171

즉, arrProfile 배열에는 정우성, 김태희, 고현정, 이문세, 하하 이렇게 5명 배우의 프로필 정보가 저장되어 있습니다.

select new 부분?

select new 부분은 LINQ 쿼리에서 조인된 결과를 원하는 형태로 가공하여 새로운 객체를 생성하는 역할을 합니다.

좀 더 쉽게 설명드리자면, 여러분이 옷장에서 옷을 꺼내 코디를 하는 것과 비슷합니다. 옷장에는 다양한 옷들이 있지만 (데이터 원본), 여러분은 원하는 옷들을 선택해서 새로운 스타일을 만들어냅니다 (새로운 객체).

이 코드에서는 select new를 통해 arrProfile 배열의 프로필 정보와 arrProduct 배열의 제품 정보 중에서 필요한 정보만 골라서 새로운 익명 객체를 만들고 있습니다.

구체적으로 select new { Name = profile.Name, Work = product.Title, Height = profile.Height } 부분을 분석해보면:

  • new { ... }:

    • 중괄호 {} 안에 새로운 객체의 프로퍼티를 정의합니다.
    • 이 경우에는 Name, Work, Height라는 세 개의 프로퍼티를 가진 익명 객체를 생성합니다. 익명 객체는 이름이 없는 객체로, 간단하게 데이터를 묶어서 사용할 때 유용합니다.
  • Name = profile.Name:

    • 새로운 객체의 Name 프로퍼티에 profile.Name 값을 할당합니다. 즉, arrProfile 배열에서 가져온 프로필의 이름을 새로운 객체의 Name 프로퍼티에 저장합니다.
  • Work = product.Title:

    • 새로운 객체의 Work 프로퍼티에 product.Title 값을 할당합니다. 즉, arrProduct 배열에서 가져온 제품의 이름을 새로운 객체의 Work 프로퍼티에 저장합니다.
  • Height = profile.Height:

    • 새로운 객체의 Height 프로퍼티에 profile.Height 값을 할당합니다. 즉, arrProfile 배열에서 가져온 프로필의 키를 새로운 객체의 Height 프로퍼티에 저장합니다.

결과적으로 select new를 통해 다음과 같은 형태의 새로운 객체가 생성됩니다.

{
    Name = "정우성",
    Work = "비트",
    Height = 186
}
{
    Name = "김태희",
    Work = "CF 다수",
    Height = 158
}
...

이처럼 select new는 LINQ 쿼리에서 데이터를 원하는 형태로 가공하고 새로운 객체를 생성하는 데 유용하게 사용됩니다. 마치 옷장에서 옷을 골라 새로운 코디를 만드는 것처럼, select new를 사용하여 데이터를 조합하고 새로운 객체를 만들 수 있습니다.


조인된 결과에서 Name, Work, Height라는 세 개의 프로퍼티를 가진 익명 객체를 생성합니다.(=조인된 결과에서 새로운 익명 객체를 생성하여 Name, Work, Height 프로퍼티를 선택합니다.) ?

"조인된 결과에서 새로운 익명 객체를 생성하여 Name, Work, Height 프로퍼티를 선택합니다." 라는 말은

"조인된 결과에서 Name, Work, Height라는 세 개의 프로퍼티를 가진 익명 객체를 생성합니다." 라는 말과 동일한 의미입니다.

조금 더 풀어서 설명해 드리면,

  1. 조인된 결과: arrProfile 배열과 arrProduct 배열을 조인한 결과를 의미합니다. 즉, 두 배열에서 일치하는 배우 이름을 가진 프로필 정보와 제품 정보가 합쳐진 데이터입니다.

  2. 새로운 익명 객체 생성: new { ... } 를 사용하여 새로운 객체를 생성합니다. 이때 익명 객체는 클래스를 정의하지 않고 바로 객체를 생성하는 방식입니다.

  3. Name, Work, Height 프로퍼티 선택: 새로운 익명 객체 안에 Name, Work, Height 라는 세 개의 프로퍼티를 정의하고, 조인된 결과에서 해당하는 값을 각 프로퍼티에 할당합니다.

즉, select new 부분은 조인된 결과를 사용해서 새로운 익명 객체를 만들고, 이 객체는 Name, Work, Height 세 개의 프로퍼티를 가지게 됩니다.

0개의 댓글