LINQ 筛选、排序、分组与联接完整示例
C# 中用 LINQ 处理数据,可按“数据源 → Where 筛选 → OrderBy/ThenBy 排序 → GroupBy 分组聚合 → Join 关联”的思路组合操作。这些操作步骤不必每次全部采用;先明确最终应当什么形状的结果,再选操作符,能避免为了“用 LINQ”而制造不必要的中间集合。

Microsoft C# 文档将查询定义为描述从数据源取回何种数据的指令集;查询变量和实际执行时点并不总是同一件事。
准备一组贯穿数据
record Department(int Id, string Name);
record Employee(int Id, string Name, int DepartmentId, decimal Salary, bool Active);
var departments = new[]
{
new Department(1, "Engineering"),
new Department(2, "Support"),
new Department(3, "Sales")
};
var employees = new[]
{
new Employee(1, "Ada", 1, 18000m, true),
new Employee(2, "Bo", 1, 15000m, true),
new Employee(3, "Chen", 2, 12000m, false),
new Employee(4, "Ding", 2, 13500m, true),
new Employee(5, "Evan", 3, 16000m, true)
};
下面所有示例都使用这两个数组,这样可以看清每一步如何改变结果。
筛选:Where
找出仍在职且月薪不低于 15000 的员工:
var selected = employees.Where(employee =>
employee.Active && employee.Salary >= 15000m);
Where 接收一个返回布尔值的条件函数。对于 LINQ to Objects,结果在枚举时逐项判断;此时 selected 还不是已经复制好的新列表。
排序:OrderBy 与 ThenBy
从实际处理来看,先按部门升序,部门内再按薪资降序,同薪资按姓名升序:
var ordered = employees
.Where(employee => employee.Active)
.OrderBy(employee => employee.DepartmentId)
.ThenByDescending(employee => employee.Salary)
.ThenBy(employee => employee.Name);
第一个排序键用 OrderBy 或 OrderByDescending,后续键必须用 ThenBy 系列。如果连续调用两次 OrderBy,第二次会建立新的主排序,不是在第一个键内再排。
分组:GroupBy
按部门统计在职人数、平均薪资和最高薪资:
var summaries = employees
.Where(employee => employee.Active)
.GroupBy(employee => employee.DepartmentId)
.Select(group => new
{
DepartmentId = group.Key,
Count = group.Count(),
AverageSalary = group.Average(employee => employee.Salary),
MaxSalary = group.Max(employee => employee.Salary)
})
.OrderBy(summary => summary.DepartmentId)
.ToList();
GroupBy 返回一组 IGrouping<TKey,TElement>,group.Key 是分组键,group 本身可枚举该组内元素。用 Select 立即投影为统计结果,能让后续代码不必携带整组原始员工。
联接:Join
将员工的 DepartmentId 与部门的 Id 相等项联接,输出部门名:
var directory = employees
.Where(employee => employee.Active)
.Join(
departments,
employee => employee.DepartmentId,
department => department.Id,
(employee, department) => new
{
employee.Name,
Department = department.Name,
employee.Salary
})
.OrderBy(row => row.Department)
.ThenByDescending(row => row.Salary)
.ToList();
从实际处理来看,Join 的四个关键参数依次是:右侧数据源、左侧键、右侧键、结果投影。它是内联接,没有匹配部门的员工不会出现。
如果需要“所有部门,即使没有员工也要显示”,先用 GroupJoin 得到每个部门的员工组:
var departmentCounts = departments.GroupJoin(
employees.Where(employee => employee.Active),
department => department.Id,
employee => employee.DepartmentId,
(department, members) => new
{
Department = department.Name,
ActiveCount = members.Count()
});
Sales 等有员工的部门会显示真实数量,没有匹配项的部门会得到 0。
把四种操作合成一份报表
要生成“各部门高薪在职员工数和平均薪资”,可先筛选,再按部门分组,然后与部门表联接,最后排序:
var salaryGroups = employees
.Where(employee => employee.Active && employee.Salary >= 13000m)
.GroupBy(employee => employee.DepartmentId)
.Select(group => new
{
DepartmentId = group.Key,
Count = group.Count(),
Average = group.Average(employee => employee.Salary)
});
var report = salaryGroups
.Join(
departments,
group => group.DepartmentId,
department => department.Id,
(group, department) => new
{
Department = department.Name,
group.Count,
group.Average
})
.OrderByDescending(row => row.Average)
.ThenBy(row => row.Department)
.ToList();
ToList() 是这份报表的执行边界。它之前是查询定义,之后是当时结果的快照。
延迟执行和数据库 provider
从实际处理来看,很多返回 IEnumerable<T> 或 IQueryable<T> 的 LINQ 操作只构建查询,到 foreach、ToList、Count 等枚举/终结操作时才执行。因此:
- 源集合在枚举前改变,结果也可能改变;
- 多次枚举可能多次计算,对数据库则可能多次发 SQL;
- 需要稳定快照时,在正确边界调用
ToList(); - 对
IQueryable<T>,自定义 C# 方法未必能翻译为 SQL,应查看 provider 的支持和实际生成查询。
结果不对时怎么查
- 排序次序不对:检查第二个键是否误用了
OrderBy而非ThenBy。 - 联接后少数据:确认是否真的需要内联接,并检查两侧键的类型和值。
- 分组统计重复:检查是否在一对多 Join 放大行数后才做
Count,需要时先选取唯一标识。 - 每次输出都不一样:检查延迟执行期间源数据是否仍在变化,需要快照时及时物化。
总结
LINQ 的关键不是背下所有操作符,而是按结果形状组织步骤:Where 决定保留哪些项,OrderBy/ThenBy 决定顺序,GroupBy 将相同键变成组,Join 用共同键合并两个数据源。最后明确执行边界,并根据 LINQ to Objects 还是数据库 provider 验证实际行为。
-
09.21
LINQ 筛选、排序、分组与联接完整示例
-
09.21
NHibernate 3 lambda-configuration 属性配置方法
-
09.21
NHibernate 2.1.2 延迟加载配置与验证教程
-
09.21
Flutter BoxConstraints 无限高度报错怎么修复
-
09.21
ASP.NET 缓存与 GZIP 页面性能优化教程
-
09.21
NHibernate 3 fluent-configuration 属性配置教程
-
-
下载
- |
-
-
下载
- 《行尸走肉第一章》免安装中文汉化硬盘版下载
- 单机|436 MB
- 一款以动作冒险为主题的游戏
-
-
下载
- 《街头霸王X铁拳》免安装中文汉化硬盘版下载
- 单机|111MB
- 一款非常好玩的格斗游戏
-
-
下载
- |
-
-
下载
- 《暗黑破坏神3》免安装繁体中文正式版下载
- 单机|7630 MB
- 一款以角色扮演为主题的游戏
-
-
下载
- 《马克思佩恩3》免安装硬盘版下载
- 单机|27033 MB
- 一款以第三人称射击为主题的游戏