结果集交集(Intersect)

核心概念

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)

执行结果示意

集合条件结果
q1ID > 3ID: 4, 5, 6, 7 …
q2ID < 5ID: 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 更简洁且全程在数据库端执行,不需要先物化任何结果。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注