在关系数据库术语中,内部联接生成一个结果集,其中第一个集合的每个元素对于第二个集合中的每个匹配元素都显示一次。如果第一个集合中的元素没有匹配的元素,则它不会出现在结果集中。join方法(由c中的join子句调用)实现内部联接。
本主题介绍如何执行内部联接的四种变体:
-
将来自两个数据源的元素关联起来的简单内部联接
基于一个简单的键。
-
一种内部联接,将来自两个数据源的元素基于
复合键。复合键,它是由
多个值,使您能够基于多个
不止一个财产。
-
一种多重连接,其中连续的连接操作附加到
彼此。
-
通过使用组联接实现的内部联接。
例子
简单键联接示例
下面的示例创建了两个集合,其中包含两个用户定义类型的对象:person和pet。该查询使用C中的join子句将Person对象与拥有该对象的pet对象匹配。C中的select子句定义结果对象的外观。在本例中,结果对象是匿名类型,由所有者的名字和宠物的名字组成。
C.*
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Pet
{
public string Name { get; set; }
public Person Owner { get; set; }
}
/// <summary>
/// Simple inner join.
/// </summary>
public static void InnerJoinExample()
{
Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };
Pet barley = new Pet { Name = "Barley", Owner = terry };
Pet boots = new Pet { Name = "Boots", Owner = terry };
Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
Pet bluemoon = new Pet { Name = "Blue Moon", Owner = rui };
Pet daisy = new Pet { Name = "Daisy", Owner = magnus };
// Create two lists.
List<Person> people = new List<Person> { magnus, terry, charlotte, arlene, rui };
List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };
// 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("\"{0}\" is owned by {1}", ownerAndPet.PetName, ownerAndPet.OwnerName);
}
}
// This code produces the following 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
注意,姓氏为“huff”的person对象不会出现在结果集中,因为没有pet.owner等于该人的pet对象。
例子
组合键联接示例
您可以使用复合键来比较基于多个属性的元素,而不是仅基于一个属性关联元素。为此,请为每个集合指定键选择器函数,以返回由要比较的属性组成的匿名类型。如果为属性添加标签,则它们在每个键的匿名类型中必须具有相同的标签。属性的显示顺序也必须相同。
下面的示例使用员工对象列表和学生对象列表来确定哪些员工也是学生。这两种类型都有字符串类型的firstname和lastname属性。从每个列表元素创建联接键的函数返回一个匿名类型,该类型由每个元素的firstname和lastname属性组成。join操作比较这些组合键是否相等,并从每个列表中返回两对对象,其中名和姓都匹配。
C.*
class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int EmployeeID { get; set; }
}
class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int StudentID { get; set; }
}
/// <summary>
/// Performs a join operation using a composite key.
/// </summary>
public static void CompositeKeyJoinExample()
{
// Create a list of employees.
List<Employee> employees = new List<Employee> {
new Employee { FirstName = "Terry", LastName = "Adams", EmployeeID = 522459 },
new Employee { FirstName = "Charlotte", LastName = "Weiss", EmployeeID = 204467 },
new Employee { FirstName = "Magnus", LastName = "Hedland", EmployeeID = 866200 },
new Employee { FirstName = "Vernette", LastName = "Price", EmployeeID = 437139 } };
// Create a list of students.
List<Student> students = new List<Student> {
new Student { FirstName = "Vernette", LastName = "Price", StudentID = 9562 },
new Student { FirstName = "Terry", LastName = "Earls", StudentID = 9870 },
new Student { FirstName = "Terry", LastName = "Adams", StudentID = 9913 } };
// Join the two data sources based on a composite key consisting of first and last name,
// to determine which employees are also students.
IEnumerable<string> 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);
}
// This code produces the following output:
//
// The following people are both employees and students:
// Terry Adams
// Vernette Price
例子
多重连接示例
任何数量的连接操作都可以相互附加以执行多个连接。C中的每个join子句都将指定的数据源与前一个join的结果关联起来。
下面的示例创建三个集合:人员对象列表、cat对象列表和dog对象列表。
C中的第一个join子句基于匹配cat.owner的person对象来匹配people和cat。它返回包含Person对象和cat.name的匿名类型序列。
C中的第二个join子句将第一个join返回的匿名类型与提供的狗列表中的狗对象相关联,基于复合键,该复合键由类型person的owner属性和动物名称的第一个字母组成。它返回一系列匿名类型,其中包含每个匹配对的cat.name和dog.name属性。因为这是一个内部联接,所以只返回第一个数据源中与第二个数据源匹配的那些对象。
C.*
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Pet
{
public string Name { get; set; }
public Person Owner { get; set; }
}
class Cat : Pet
{ }
class Dog : Pet
{ }
public static void MultipleJoinExample()
{
Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };
Person phyllis = new Person { FirstName = "Phyllis", LastName = "Harris" };
Cat barley = new Cat { Name = "Barley", Owner = terry };
Cat boots = new Cat { Name = "Boots", Owner = terry };
Cat whiskers = new Cat { Name = "Whiskers", Owner = charlotte };
Cat bluemoon = new Cat { Name = "Blue Moon", Owner = rui };
Cat daisy = new Cat { Name = "Daisy", Owner = magnus };
Dog fourwheeldrive = new Dog { Name = "Four Wheel Drive", Owner = phyllis };
Dog duke = new Dog { Name = "Duke", Owner = magnus };
Dog denim = new Dog { Name = "Denim", Owner = terry };
Dog wiley = new Dog { Name = "Wiley", Owner = charlotte };
Dog snoopy = new Dog { Name = "Snoopy", Owner = rui };
Dog snickers = new Dog { Name = "Snickers", Owner = arlene };
// Create three lists.
List<Person> people =
new List<Person> { magnus, terry, charlotte, arlene, rui, phyllis };
List<Cat> cats =
new List<Cat> { barley, boots, whiskers, bluemoon, daisy };
List<Dog> dogs =
new List<Dog> { fourwheeldrive, duke, denim, wiley, snoopy, snickers };
// 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 \"{0}\" shares a house, and the first letter of their name, with \"{1}\".",
obj.CatName, obj.DogName);
}
}
// This code produces the following 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".
例子
使用分组联接示例进行内部联接
下面的示例演示如何使用组联接实现内部联接。
在查询1中,根据pet.owner属性匹配的人员,将人员对象列表分组加入宠物对象列表。组联接创建一个中间组集合,其中每个组由一个人对象和一系列匹配的宠物对象组成。
通过向查询中添加第二个FROM子句,这个序列组合(或扁平)成一个较长的序列。最终序列元素的类型由select子句指定。在本例中,该类型是一个匿名类型,由每个匹配对的person.firstname和pet.name属性组成。
query1的结果相当于使用join子句而不使用into子句执行内部联接所获得的结果集。query2变量演示了这个等价的查询。
C.*
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Pet
{
public string Name { get; set; }
public Person Owner { get; set; }
}
/// <summary>
/// Performs an inner join by using GroupJoin().
/// </summary>
public static void InnerGroupJoinExample()
{
Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
Pet barley = new Pet { Name = "Barley", Owner = terry };
Pet boots = new Pet { Name = "Boots", Owner = terry };
Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
Pet daisy = new Pet { Name = "Daisy", Owner = magnus };
// Create two lists.
List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };
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("{0} - {1}", 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("\nThe equivalent operation using Join():");
foreach (var v in query2)
Console.WriteLine("{0} - {1}", v.OwnerName, v.PetName);
}
// This code produces the following 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
编译代码
-
在Visual Studio中创建新的控制台应用程序项目。
-
添加对System.Core.dll的引用(如果尚未引用)。
-
包括System.Linq命名空间。
-
将示例中的代码复制并粘贴到program.cs文件中,
在主方法下面。向要调用的主方法添加一行代码
您粘贴的方法。
-
运行程序。