核心概念
Intersect 用于取两个结果集的交集——即同时存在于两个集合中的元素,等效于 SQL 中的 INTERSECT。
LINQ 中的写法
using (var db = new DataClasses1DataContext())
{
var q1 = from s in db.Student
where s.ID > 3
select s;
var r1 = q1.ToList();
var q2 = from s in db.Student
where s.ID < 5
select s;
var r2 = q2.ToList();
// 对 IQueryable 调用 Intersect —— 翻译为 SQL,在数据库端执行
var q = q1.Intersect(q2);
var r = q.ToList();
// 对 List 调用 Intersect —— 全表加载到内存后操作,不推荐
var r3 = r1.Intersect(r2);
}Code language: JavaScript (javascript)
执行结果示意
| 集合 | 条件 | 结果 |
|---|---|---|
q1 | ID > 3 | ID: 4, 5, 6, 7 … |
q2 | ID < 5 | ID: 1, 2, 3, 4 |
Intersect | 同时满足 | ID: 4 |
生成的 SQL
SELECT DISTINCT [t0].[ID], [t0].[Name], [t0].[ClassID], [t0].[AddTime], [t0].[Status]
FROM [dbo].[Student] AS [t0]
WHERE
(EXISTS(SELECT NULL AS [EMPTY] FROM [dbo].[Student] AS [t1] WHERE ([t0].[ID] = [t1].[ID]) AND ([t1].[ID] < 5) ))
AND
([t0].[ID] > 3)Code language: PHP (php)
生成的 SQL 使用
EXISTS子查询实现交集判断,而非 SQL Server 原生的INTERSECT关键字。结果自动去重(DISTINCT)。
IQueryable vs IEnumerable(List)调用对比
| 调用方式 | 执行位置 | SQL 生成 | 推荐度 |
|---|---|---|---|
q1.Intersect(q2)(IQueryable) | 数据库端 | EXISTS 子查询 | 推荐 |
r1.Intersect(r2)(List<T>) | 内存中 | 先 SELECT * 全表加载 | 不推荐 |
为什么不要在
List上调用:ToList()已经把数据全部拉到内存,交集在客户端内存中计算,浪费网络传输和内存。应始终对IQueryable调用Intersect,让数据库完成计算。
不同表取交集
与 Union / Concat 一样,两边元素类型必须一致:
// 取同时在 Customers 和 Employees 中出现的国家
var q = (from c in db.Customers
select c.Country)
.Intersect(
from e in db.Employees
select e.Country
);Code language: JavaScript (javascript)
与
Where+Contains的对比:// 写法一:Intersect(推荐,翻译为 EXISTS) var q = q1.Intersect(q2); // 写法二:Where + Contains(先加载 q2 到内存再 IN 查询) var ids = q2.Select(s => s.ID).ToList(); var q = q1.Where(s => ids.Contains(s.ID));Code language: JavaScript (javascript)
Intersect更简洁且全程在数据库端执行,不需要先物化任何结果。
Previous: 结果集合并(UNION / UNION ALL)
Next: 结果集差集(Except)