내부 조인 수행

관계형 데이터베이스 용어에서 내부 조인은 첫 번째 컬렉션의 각 요소가 두 번째 컬렉션에서 일치하는 모든 요소에 대해 한 번 표시되는 결과 집합을 생성합니다. 첫 번째 컬렉션의 요소에 일치하는 요소가 없는 경우에는 결과 집합에 표시되지 않습니다. C#에서 join 절에 의해 호출되는 Join 메서드는 내부 조인을 구현합니다.

이 문서에서는 내부 조인의 네 가지 변환을 수행하는 방법을 보여 줍니다.

  • 단순 키에 따라 두 데이터 소스의 요소를 상호 연결하는 간단한 내부 조인

  • 복합 키에 따라 두 데이터 소스의 요소를 상호 연결하는 내부 조인. 둘 이상의 값으로 구성된 키인 복합 키를 사용하면 둘 이상의 속성에 따라 요소를 상호 연결할 수 있습니다.

  • 연속 조인 작업이 서로 추가되는 여러 조인

  • 그룹 조인을 사용하여 구현되는 내부 조인

참고

이 항목의 예제에서는 다음 데이터 클래스를 사용합니다.

record Person(string FirstName, string LastName);
record Pet(string Name, Person Owner);
record Employee(string FirstName, string LastName, int EmployeeID);
record Cat(string Name, Person Owner) : Pet(Name, Owner);
record Dog(string Name, Person Owner) : Pet(Name, Owner);

및 개체 컬렉션Student 쿼리의 클래스입니다.

예제 - 단순 키 조인

다음 예제에서는 두 개의 사용자 정의 형식인 PersonPet의 개체를 포함하는 두 개의 컬렉션을 만듭니다. 쿼리는 C#의 join 절을 사용하여 Person 개체를 Owner가 해당 PersonPet 개체와 일치시킵니다. C#의 select 절은 결과 개체의 모양을 정의합니다. 이 예제에서 결과 개체는 소유자의 이름과 애완 동물의 이름으로 구성된 무명 형식입니다.

Person magnus = new(FirstName: "Magnus", LastName: "Hedlund");
Person terry = new("Terry", "Adams");
Person charlotte = new("Charlotte", "Weiss");
Person arlene = new("Arlene", "Huff");
Person rui = new("Rui", "Raposo");

List<Person> people = new() { magnus, terry, charlotte, arlene, rui };

List<Pet> pets = new()
{
    new(Name: "Barley", Owner: terry),
    new("Boots", terry),
    new("Whiskers", charlotte),
    new("Blue Moon", rui),
    new("Daisy", magnus),
};

// Create a collection of person-pet pairs. Each element in the collection
// is an anonymous type containing both the person's name and their pet's name.
var query =
    from person in people
    join pet in pets on person equals pet.Owner
    select new
    {
        OwnerName = person.FirstName,
        PetName = pet.Name
    };

foreach (var ownerAndPet in query)
{
    Console.WriteLine($"\"{ownerAndPet.PetName}\" is owned by {ownerAndPet.OwnerName}");
}

/* Output:
     "Daisy" is owned by Magnus
     "Barley" is owned by Terry
     "Boots" is owned by Terry
     "Whiskers" is owned by Charlotte
     "Blue Moon" is owned by Rui
*/

LastName이 "Huff"인 Person 개체는 Pet.OwnerPerson과 같은 Pet 개체가 없기 때문에 결과 집합에 표시되지 않습니다.

예제 - 복합 키 조인

속성 하나만 기준으로 요소를 상호 연결하는 대신 복합 키를 사용하여 여러 속성을 기준으로 요소를 비교할 수 있습니다. 이렇게 하려면 비교하려는 속성으로 구성된 무명 형식을 반환할 각 컬렉션에 대한 키 선택기 함수를 지정합니다. 속성에 레이블을 지정하는 경우 각 키의 무명 형식에 동일한 레이블이 있어야 합니다. 또한 속성은 동일한 순서로 나타나야 합니다.

다음 예제에서는 Employee 개체 목록과 Student 개체 목록을 사용하여 학생이기도 한 직원을 확인합니다. 이러한 형식에는 둘 다 String 형식의 FirstNameLastName 속성이 있습니다. 각 목록의 요소에서 조인 키를 만드는 함수는 각 요소의 FirstNameLastName 속성으로 구성된 무명 형식을 반환합니다. 조인 작업은 이러한 복합 키가 같은지 비교하고 각 목록에서 이름과 성이 둘 다 일치하는 개체 쌍을 반환합니다.

List<Employee> employees = new()
{
    new(FirstName: "Terry", LastName: "Adams", EmployeeID: 522459),
    new("Charlotte", "Weiss", 204467),
    new("Magnus", "Hedland", 866200),
    new("Vernette", "Price", 437139)
};

List<Student> students = new()
{
    new(FirstName: "Vernette", LastName: "Price", StudentID: 9562),
    new("Terry", "Earls", 9870),
    new("Terry", "Adams", 9913)
};

// Join the two data sources based on a composite key consisting of first and last name,
// to determine which employees are also students.
var query =
    from employee in employees
    join student in students on new
    {
        employee.FirstName,
        employee.LastName
    } equals new
    {
        student.FirstName,
        student.LastName
    }
    select employee.FirstName + " " + employee.LastName;

Console.WriteLine("The following people are both employees and students:");
foreach (string name in query)
{
    Console.WriteLine(name);
}

/* Output:
    The following people are both employees and students:
    Terry Adams
    Vernette Price
 */

예제 - 여러 조인

개수에 제한없이 조인 작업을 서로 추가하여 여러 조인을 수행할 수 있습니다. C#의 각 join 절은 지정된 데이터 소스를 이전 조인의 결과와 상호 연결합니다.

다음 예제에서는 Person 개체 목록, Cat 개체 목록, Dog 개체 목록의 세 가지 컬렉션을 만듭니다.

C#의 첫 번째 join 절은 Cat.Owner와 일치하는 Person을 기준으로 사람과 고양이를 일치시킵니다. Person 개체 및 Cat.Name을 포함하는 무명 형식의 시퀀스를 반환합니다.

C#의 두 번째 join 절은 Person 형식의 Owner 속성과 동물 이름의 첫 글자로 구성된 복합 키를 기준으로 제공된 개 목록의 Dog 개체와 첫 번째 조인에서 반환된 무명 형식을 상호 연결합니다. 일치하는 각 쌍의 Cat.NameDog.Name 속성을 포함하는 무명 형식의 시퀀스를 반환합니다. 내부 조인이기 때문에 두 번째 데이터 소스에 일치 항목이 있는 첫 번째 데이터 소스의 개체만 반환됩니다.

Person magnus = new(FirstName: "Magnus", LastName: "Hedlund");
Person terry = new("Terry", "Adams");
Person charlotte = new("Charlotte", "Weiss");
Person arlene = new("Arlene", "Huff");
Person rui = new("Rui", "Raposo");
Person phyllis = new("Phyllis", "Harris");

List<Person> people = new() { magnus, terry, charlotte, arlene, rui, phyllis };

List<Cat> cats = new()
{
    new(Name: "Barley", Owner: terry),
    new("Boots", terry),
    new("Whiskers", charlotte),
    new("Blue Moon", rui),
    new("Daisy", magnus),
};

List<Dog> dogs = new()
{
    new(Name: "Four Wheel Drive", Owner: phyllis),
    new("Duke", magnus),
    new("Denim", terry),
    new("Wiley", charlotte),
    new("Snoopy", rui),
    new("Snickers", arlene),
};

// The first join matches Person and Cat.Owner from the list of people and
// cats, based on a common Person. The second join matches dogs whose names start
// with the same letter as the cats that have the same owner.
var query =
    from person in people
    join cat in cats on person equals cat.Owner
    join dog in dogs on new
    {
        Owner = person,
        Letter = cat.Name.Substring(0, 1)
    } equals new
    {
        dog.Owner,
        Letter = dog.Name.Substring(0, 1)
    }
    select new
    {
        CatName = cat.Name,
        DogName = dog.Name
    };

foreach (var obj in query)
{
    Console.WriteLine(
        $"The cat \"{obj.CatName}\" shares a house, and the first letter of their name, with \"{obj.DogName}\"."
    );
}

/* Output:
     The cat "Daisy" shares a house, and the first letter of their name, with "Duke".
     The cat "Whiskers" shares a house, and the first letter of their name, with "Wiley".
 */

예제 - 그룹화된 조인을 사용하는 내부 조인

다음 예제에서는 그룹 조인을 사용하여 내부 조인을 구현하는 방법을 보여 줍니다.

query1에서 Person 개체 목록은 Pet.Owner 속성과 일치하는 Person을 기준으로 Pet 개체 목록에 그룹 조인됩니다. 그룹 조인은 각 그룹이 Person 개체 및 일치하는 Pet 개체 시퀀스로 구성된 중간 그룹 컬렉션을 만듭니다.

두 번째 from 절을 쿼리에 추가하여 이 시퀀스의 시퀀스를 더 긴 시퀀스에 결합(또는 평면화)할 수 있습니다. 최종 시퀀스의 요소 형식은 select 절에 의해 지정됩니다. 이 예제에서 해당 형식은 일치하는 각 쌍의 Person.FirstNamePet.Name 속성으로 구성된 무명 형식입니다.

query1의 결과는 into 절 없이 join 절을 사용해서 내부 조인을 수행하여 얻은 결과 집합과 같습니다. query2 변수는 이와 동일한 쿼리를 보여 줍니다.

Person magnus = new(FirstName: "Magnus", LastName: "Hedlund");
Person terry = new("Terry", "Adams");
Person charlotte = new("Charlotte", "Weiss");
Person arlene = new("Arlene", "Huff");

List<Person> people = new() { magnus, terry, charlotte, arlene };

List<Pet> pets = new()
{
    new(Name: "Barley", Owner: terry),
    new("Boots", terry),
    new("Whiskers", charlotte),
    new("Blue Moon", terry),
    new("Daisy", magnus),
};

var query1 =
    from person in people
    join pet in pets on person equals pet.Owner into gj
    from subpet in gj
    select new
    {
        OwnerName = person.FirstName,
        PetName = subpet.Name
    };

Console.WriteLine("Inner join using GroupJoin():");
foreach (var v in query1)
{
    Console.WriteLine($"{v.OwnerName} - {v.PetName}");
}

var query2 =
    from person in people
    join pet in pets on person equals pet.Owner
    select new
    {
        OwnerName = person.FirstName,
        PetName = pet.Name
    };

Console.WriteLine();
Console.WriteLine("The equivalent operation using Join():");
foreach (var v in query2)
{
    Console.WriteLine($"{v.OwnerName} - {v.PetName}");
}

/* Output:
    Inner join using GroupJoin():
    Magnus - Daisy
    Terry - Barley
    Terry - Boots
    Terry - Blue Moon
    Charlotte - Whiskers

    The equivalent operation using Join():
    Magnus - Daisy
    Terry - Barley
    Terry - Boots
    Terry - Blue Moon
    Charlotte - Whiskers
*/

참조