LINQ 按同一列连接多个表
LINQ join multiple tables by same column
我有一个 table 发短信,我需要加入另外两个 table 学生和 Staff 搜索有关这 2 table 的信息。
学生 字段:
- 编号
- 姓名
- ...以及一些学生特有的其他字段
员工 字段:
- 编号
- 姓名
- ...以及一些特定于员工的其他字段
发短信 字段:
- 编号
- PersonId // 包含学生 ID 或员工 ID
- PersonTypeId // 表示PersonId是student还是staff类型(student = 1, staff = 2)
现在我需要编写一个 linq 查询来搜索 table 按学生或教职员工姓名发送的短信,但我无法通过 linq 来实现此目的。
var query = (from t in texting
join s in studentBo.GetListQuery()
on t.PersonId equals s.Id
join st in staffBo.GetListQuery()
on t.PersonId equals st.Id
where ...
select t);
这将 table 连接在一起,但它不关心 PersonId 类型是什么,所以它们都是混合的。我如何指定以便它根据正确的 PersonTypeId 正确加入 PersonId?似乎没有其他东西可以附加在 on 子句或 where 子句上来实现这一点 = (.
您将不得不合并 Student 和 Staff 表,否则您的所有查询都会太复杂,因为您将不得不使用 Union
Person
Id
Name
PersonType
Texting
Id
PersonId
并查询
var query = (from t in texting
join p in person
on t.PersonId equals p.Id
where ...
select t);
PS 如果您仍然想要使用 2 个表而不是一个表进行查询,则必须 post 真正的代码。
您需要将这些作为两个单独的查询来执行,投影到新类型,然后合并结果。乱七八糟,但方法如下。
首先让你的学生:
var textingStudents = (
from s in students
join t in texting on s.Id equals t.PersonId
where t.PersonTypeId == 1
select new { id = s.Id, personTypeId = 1, name = s.Name }).ToList();
现在以几乎完全相同的方式让您的员工:
var textingStaff = (
from s in staff
join t in texting on s.Id equals t.PersonId
where t.PersonTypeId == 2
select new { id = s.Id, personTypeId = 2, name = s.Name }).ToList();
现在您可以合并两者:
var allTextingPeople = textingStudents.Union(textingStaff);
如果您需要其他属性,则将 then 添加到 select
语句中声明的匿名类型 - 请记住,该类型需要在 textingStudents
和 [=15] 中具有相同的属性=] 结果。或者,定义一个 class 并在两个查询中执行 select new MyUnionClass { ... }
。
编辑
使用您概述的当前方法,您可能会陷入一个受伤的世界。如果您使用的是关系数据库(即 sql 服务器),您几乎可以肯定没有在 Texting
table 上定义诸如外键之类的约束,这意味着您最终会遇到 ID 冲突和以后肯定会出现错误。最好的方法可能是用一个 table 来表示 Staff
和 Student
(我们称它为 Person
并用一列定义人员的“类型”——该列本身将是外键 link 到另一个 table 与您的列表 PersonTypes
所以您有一个 name
,并且您想要所有引用具有此名称的 Student
的 Textings
,以及所有引用 Textings
成员的 Textings
Staff
使用此名称。
我的建议是将学生短信与员工短信合并。你可以在一个大的 LINQ 语句中做到这一点,但是这会使它很难理解。所以我将分两步完成,然后在一个查询中连接它:
const int student = 1;
string name = "William Shakespeare";
var studentTextings = textings.Where(texting => texting.PersonTypeId == student)
.Join(students.Where(student => student.Name == name),
texting => texting.PersonId, // from every Texting take the foreign key
student => student.Id, // from every Student take the primary key
// parameter resultSelector:
// from every texting with its matching student make one new:
(texting, studentWithThisTexting) => new
{
// Select the Texting properties that you plan to use
Id = texting.Id,
...
}
换句话说:在所有短信中,只保留那些引用学生的短信,所以你知道外键指的是学生table中的主键。从所有学生中,只保留那些具有请求名称的学生。
加入所有剩余的短信和少数在主键和匹配外键上具有此名称的学生。
为员工做类似的事情:
const int staff = 2;
var staffTextings = textings.Where(texting => texting.PersonTypeId == staff)
.Join(staffMembers.Where(staffMember => staffMember.Name == name),
texting => texting.PersonId, // from every Texting take the foreign key
staffMember => staffMember.Id, // from every Staff member take the primary key
// parameter resultSelector:
(texting, staffMembers) => new
{
// Select the Texting properties that you plan to use
Id = texting.Id,
...
}
现在你所要做的就是连接这两个。请注意:您只能连接相似的项目,因此两个连接中的 resultSelector 应该 select 完全相同类型的对象。
var textingsOfPersonsWithThisName = studentTextings.Concat(staffTextings);
还有改进的空间!
如果仔细观察,您会发现短信 table 将被扫描两次。之所以这样,是因为你的数据库没有规范化。
发给学生的短信会变成发给员工的短信吗?如果没有,我的建议是创建两个 table:StudentTextings 和 StaffTextings。除了查询会更快之外,因为您不必检查 PersonType
,这还有一个好处,如果稍后您决定 StudentTexting 与 StaffTexting 不同,您可以更改 tables没有 运行 问题。
如果您真的认为有时您需要更改短信的类型,并且您不想通过创建新短信来做到这一点,您还应该有两个 tables:一个与StudentTextings,一个与 StaffTextings,两者 table 都与 Texting 具有一对一的关系。
所以 Students 与 StudentTextings 一对多,而 Students 与 Textings 一对一。 Staff 和 StaffTextings 类似。
所以学生 [4] 有 3 个 ID 为 [30]、[34]、[37] 的 StudentTexting。这些 StudentTextings 中的每一个都有一个值为 [4] 的外键 StudentId。每个StudentTexting都用外键引用自己的Texting:[30]指的是texting [101],所以它有外键101,等等
现在,如果发短信 [101] 必须成为员工 [7] 的短信,您将必须删除引用 [101] 的 StudentTexting 并创建一个引用员工 [7] 和发短信的新 StaffTexting [101]
顺便说一下,由于组合 [StudentId, TextingId] 是唯一的,table StudentTextings 可以使用这个组合作为主键。类似于 StaffTextings
我有一个 table 发短信,我需要加入另外两个 table 学生和 Staff 搜索有关这 2 table 的信息。
学生 字段:
- 编号
- 姓名
- ...以及一些学生特有的其他字段
员工 字段:
- 编号
- 姓名
- ...以及一些特定于员工的其他字段
发短信 字段:
- 编号
- PersonId // 包含学生 ID 或员工 ID
- PersonTypeId // 表示PersonId是student还是staff类型(student = 1, staff = 2)
现在我需要编写一个 linq 查询来搜索 table 按学生或教职员工姓名发送的短信,但我无法通过 linq 来实现此目的。
var query = (from t in texting
join s in studentBo.GetListQuery()
on t.PersonId equals s.Id
join st in staffBo.GetListQuery()
on t.PersonId equals st.Id
where ...
select t);
这将 table 连接在一起,但它不关心 PersonId 类型是什么,所以它们都是混合的。我如何指定以便它根据正确的 PersonTypeId 正确加入 PersonId?似乎没有其他东西可以附加在 on 子句或 where 子句上来实现这一点 = (.
您将不得不合并 Student 和 Staff 表,否则您的所有查询都会太复杂,因为您将不得不使用 Union
Person
Id
Name
PersonType
Texting
Id
PersonId
并查询
var query = (from t in texting
join p in person
on t.PersonId equals p.Id
where ...
select t);
PS 如果您仍然想要使用 2 个表而不是一个表进行查询,则必须 post 真正的代码。
您需要将这些作为两个单独的查询来执行,投影到新类型,然后合并结果。乱七八糟,但方法如下。
首先让你的学生:
var textingStudents = (
from s in students
join t in texting on s.Id equals t.PersonId
where t.PersonTypeId == 1
select new { id = s.Id, personTypeId = 1, name = s.Name }).ToList();
现在以几乎完全相同的方式让您的员工:
var textingStaff = (
from s in staff
join t in texting on s.Id equals t.PersonId
where t.PersonTypeId == 2
select new { id = s.Id, personTypeId = 2, name = s.Name }).ToList();
现在您可以合并两者:
var allTextingPeople = textingStudents.Union(textingStaff);
如果您需要其他属性,则将 then 添加到 select
语句中声明的匿名类型 - 请记住,该类型需要在 textingStudents
和 [=15] 中具有相同的属性=] 结果。或者,定义一个 class 并在两个查询中执行 select new MyUnionClass { ... }
。
编辑
使用您概述的当前方法,您可能会陷入一个受伤的世界。如果您使用的是关系数据库(即 sql 服务器),您几乎可以肯定没有在 Texting
table 上定义诸如外键之类的约束,这意味着您最终会遇到 ID 冲突和以后肯定会出现错误。最好的方法可能是用一个 table 来表示 Staff
和 Student
(我们称它为 Person
并用一列定义人员的“类型”——该列本身将是外键 link 到另一个 table 与您的列表 PersonTypes
所以您有一个 name
,并且您想要所有引用具有此名称的 Student
的 Textings
,以及所有引用 Textings
成员的 Textings
Staff
使用此名称。
我的建议是将学生短信与员工短信合并。你可以在一个大的 LINQ 语句中做到这一点,但是这会使它很难理解。所以我将分两步完成,然后在一个查询中连接它:
const int student = 1;
string name = "William Shakespeare";
var studentTextings = textings.Where(texting => texting.PersonTypeId == student)
.Join(students.Where(student => student.Name == name),
texting => texting.PersonId, // from every Texting take the foreign key
student => student.Id, // from every Student take the primary key
// parameter resultSelector:
// from every texting with its matching student make one new:
(texting, studentWithThisTexting) => new
{
// Select the Texting properties that you plan to use
Id = texting.Id,
...
}
换句话说:在所有短信中,只保留那些引用学生的短信,所以你知道外键指的是学生table中的主键。从所有学生中,只保留那些具有请求名称的学生。
加入所有剩余的短信和少数在主键和匹配外键上具有此名称的学生。
为员工做类似的事情:
const int staff = 2;
var staffTextings = textings.Where(texting => texting.PersonTypeId == staff)
.Join(staffMembers.Where(staffMember => staffMember.Name == name),
texting => texting.PersonId, // from every Texting take the foreign key
staffMember => staffMember.Id, // from every Staff member take the primary key
// parameter resultSelector:
(texting, staffMembers) => new
{
// Select the Texting properties that you plan to use
Id = texting.Id,
...
}
现在你所要做的就是连接这两个。请注意:您只能连接相似的项目,因此两个连接中的 resultSelector 应该 select 完全相同类型的对象。
var textingsOfPersonsWithThisName = studentTextings.Concat(staffTextings);
还有改进的空间!
如果仔细观察,您会发现短信 table 将被扫描两次。之所以这样,是因为你的数据库没有规范化。
发给学生的短信会变成发给员工的短信吗?如果没有,我的建议是创建两个 table:StudentTextings 和 StaffTextings。除了查询会更快之外,因为您不必检查 PersonType
,这还有一个好处,如果稍后您决定 StudentTexting 与 StaffTexting 不同,您可以更改 tables没有 运行 问题。
如果您真的认为有时您需要更改短信的类型,并且您不想通过创建新短信来做到这一点,您还应该有两个 tables:一个与StudentTextings,一个与 StaffTextings,两者 table 都与 Texting 具有一对一的关系。
所以 Students 与 StudentTextings 一对多,而 Students 与 Textings 一对一。 Staff 和 StaffTextings 类似。
所以学生 [4] 有 3 个 ID 为 [30]、[34]、[37] 的 StudentTexting。这些 StudentTextings 中的每一个都有一个值为 [4] 的外键 StudentId。每个StudentTexting都用外键引用自己的Texting:[30]指的是texting [101],所以它有外键101,等等
现在,如果发短信 [101] 必须成为员工 [7] 的短信,您将必须删除引用 [101] 的 StudentTexting 并创建一个引用员工 [7] 和发短信的新 StaffTexting [101]
顺便说一下,由于组合 [StudentId, TextingId] 是唯一的,table StudentTextings 可以使用这个组合作为主键。类似于 StaffTextings