核心概念
Except 取差集——返回在第一个集合中但不在第二个集合中的元素。等效于 SQL 中的 EXCEPT(或 MINUS)。
与
Intersect的区别:Intersect取共同部分,Except取第一个集合独有的部分。
LINQ 中的写法
using (var db = new DataClasses1DataContext())
{
// q1: ID < 3 → 1, 2
var q1 = from s in db.Student
where s.ID < 3
select s;
// q2: ID < 5 → 1, 2, 3, 4
var q2 = from s in db.Student
where s.ID < 5
select s;
// q1 中有但 q2 中没有的
var r = q1.Except(q2).ToList(); // 结果:空(q1 全部在 q2 中)
// q2 中有但 q1 中没有的
var r2 = q2.Except(q1).ToList(); // 结果:ID = 3, 4
}Code language: JavaScript (javascript)
执行结果示意
| 操作 | 含义 | 结果 |
|---|---|---|
q1.Except(q2) | {1, 2} − {1, 2, 3, 4} | 空 |
q2.Except(q1) | {1, 2, 3, 4} − {1, 2} | {3, 4} |
顺序敏感:
Except不是对称的,A.Except(B)≠B.Except(A)。
生成的 SQL
q1.Except(q2) 生成的 SQL(类似 Intersect,使用 NOT EXISTS 子查询):
SELECT [t0].[ID], [t0].[Name], [t0].[ClassID], [t0].[AddTime], [t0].[Status]
FROM [dbo].[Student] AS [t0]
WHERE ([t0].[ID] < 3)
AND NOT EXISTS (
SELECT NULL AS [EMPTY]
FROM [dbo].[Student] AS [t1]
WHERE ([t1].[ID] < 5) AND ([t0].[ID] = [t1].[ID])
)Code language: PHP (php)
逻辑:从
q1的结果中排除掉那些在q2中能找到的记录。
不同表取差集
// 有客户但没有员工的 Country
var q = (from c in db.Customers
select c.Country)
.Except(
from e in db.Employees
select e.Country
);Code language: JavaScript (javascript)
三个集合操作的完整对比
| 操作 | LINQ 方法 | SQL 等效 | 说明 | 是否去重 |
|---|---|---|---|---|
| 并集 | Union() | UNION | 合并 + 去重 | 是 |
| 合并不去重 | Concat() | UNION ALL | 直接拼接 | 否 |
| 交集 | Intersect() | INTERSECT | 共同部分 | 是 |
| 差集 | Except() | EXCEPT | 第一个有、第二个没有 | 是 |
关键要点
| 要点 | 说明 |
|---|---|
| 顺序敏感 | A.Except(B) ≠ B.Except(A) |
| 自动去重 | 结果集自动 DISTINCT |
| 类型要求 | 两边元素类型必须一致 |
| 执行位置 | 对 IQueryable 调用 → 数据库端 NOT EXISTS;对 List 调用 → 内存操作 |
| 常见用途 | 查找”新增的”、”被删除的”、”独有的”记录 |
典型场景: 对比两个时间点的数据差异——
today.Except(yesterday)找出新增的,yesterday.Except(today)找出删除的。
集合操作速查
A.Union(B) // A + B(去重)
A.Concat(B) // A + B(不去重)
A.Intersect(B) // A ∩ B
A.Except(B) // A − BCode language: PHP (php)
Previous: 结果集交集(Intersect)
Next: 排序和多列排序