内部結合の実行
リレーショナル データベースでは、"内部結合" により、2 番目のコレクション内の一致するすべての要素に対して、最初のコレクションの各要素が一度表示される結果セットが生成されます。 最初のコレクション内の要素に一致する要素が存在しない場合、その要素は結果セットには表示されません。 Join メソッドは、C# の join
句によって呼び出され、内部結合を実装します。
この記事では、次の 4 種類の内部結合を実行する方法を示します。
簡単なキーに基づいて、2 つのデータ ソースの要素を関連付ける単純な内部結合。
"複合" キーに基づいて、2 つのデータ ソースの要素を関連付ける内部結合。 複合キーは複数の値で構成され、複数のプロパティに基づいて要素を関連付けることができます。
一連の結合操作が相互に追加された "複数の結合"。
グループ結合を使用して実装された内部結合。
注意
このトピックの例には、次のデータ クラスが使用されています。
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
クラス。
例 - 簡単なキーの結合
次の例は、2 つのユーザー定義型オブジェクト、Person
と Pet
が含まれた 2 つのコレクションを作成します。 クエリでは、C# の join
句を使用して、Person
オブジェクトを Owner
がこの Person
である Pet
オブジェクトを照合します。 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.Owner
がその Person
に等しい Pet
オブジェクトがないため、結果セットに表示されません。
例 - 複合キーの結合
1 つのプロパティだけに基づいて要素を関連付ける代わりに、複合キーを使用して、複数のプロパティに基づいて要素を比較できます。 これを行うには、各コレクションに対してキー セレクター関数を指定し、比較するプロパティで構成された匿名型を返します。 プロパティにラベルを付ける場合は、各キーの匿名型に同じラベルを付ける必要があります。 また、プロパティは、同じ順序で表示する必要があります。
次の例は、Employee
オブジェクトのリストと Student
オブジェクトのリストを使用して、学生でもある社員を調べます。 これらの型の両方に、String 型の FirstName
プロパティと LastName
プロパティがあります。 それぞれのリストの要素から結合キーを作成する関数が、各要素の FirstName
プロパティと LastName
プロパティで構成された匿名型を返します。 結合操作により、これらの複合キーが等しいかどうか比較され、それぞれのリストの氏名が一致するオブジェクトのペアが返されます。
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
オブジェクトのリストの 3 つのコレクションを作成します。
C# の最初の join
句では、Cat.Owner
と一致する Person
オブジェクトに基づいて飼い主と猫を一致させます。 この操作で、Person
オブジェクトと Cat.Name
が含まれた匿名型のシーケンスが返されます。
C# の 2 番目の join
句では、Person
型の Owner
プロパティと動物の名前の最初の文字で構成される複合キーに基づいて、最初の結合で返された匿名型を、指定された犬のリストの Dog
オブジェクトに関連付けます。 この操作で、一致するそれぞれのペアの Cat.Name
プロパティと Dog.Name
プロパティが含まれた匿名型のシーケンスが返されます。 これは内部結合であるため、2 番目のデータ ソースに一致するものが存在する、最初のデータ ソースのオブジェクトのみが返されます。
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
オブジェクトのシーケンスで構成された、中間グループのコレクションが作成されます。
2 番目の from
句をクエリに追加すると、シーケンスのシーケンスが 1 つの長いシーケンスに結合 (または平坦化) されます。 最後のシーケンスの要素の型は、select
句で指定されます。 この例では、この型は、一致する各ペアの Person.FirstName
プロパティと Pet.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
*/