分组聚合:将一对多关联结果合并到一行
问题回顾
在上一节的左连接示例中,ProductName 无法直接显示出来,原因是一个学生可能对应多个产品。分组后如果直接 select,每条匹配记录都会单独成行,无法将多个产品合并展示。
核心思路: 对未分组的行进行聚合合并,把同一个学生的所有产品信息压缩到一行中显示。
LINQ 实现代码
static void Main(string[] args)
{
using (var db = new DataClasses1DataContext())
{
var query = from s in db.Student
join p in db.Product
on s.ID equals p.Sid
group new { s, p } by new { s.ID, s.Name } into g
let productNames = g.Select(m => m.p.Name + ":" + m.p.Price)
select new
{
g.Key.ID,
g.Key.Name,
info = string.Join(",", productNames)
};
var result = query.ToList();
}
}Code language: JavaScript (javascript)
代码逐步解析
| 步骤 | 代码 | 说明 |
|---|---|---|
| ① 连接 | join p in db.Product on s.ID equals p.Sid | 内连接学生表与产品表,一个学生匹配多条产品记录 |
| ② 分组 | group new { s, p } by new { s.ID, s.Name } into g | 按学生 ID 和姓名分组,将同一学生的所有 {学生, 产品} 组合归入组 g |
| ③ 投影 | let productNames = g.Select(m => m.p.Name + ":" + m.p.Price) | 从每组中提取产品信息,格式为 "产品名:价格" |
| ④ 聚合 | info = string.Join(",", productNames) | 将当前学生的所有产品字符串用逗号拼接成一个字符串 |
执行逻辑示意
原始匹配结果(多行):
┌──────┬──────────┬───────┐
│ 学生 │ 产品名 │ 价格 │
├──────┼──────────┼───────┤
│ 张三 │ 沙发 │ 999 │
│ 张三 │ 跑步机 │ 888 │
│ 李四 │ 哑铃 │ 299 │
└──────┴──────────┴───────┘
分组聚合后(一行一学生):
┌──────┬──────────────────────────────┐
│ 学生 │ info │
├──────┼──────────────────────────────┤
│ 张三 │ 沙发:999,跑步机:888 │
│ 李四 │ 哑铃:299 │
└──────┴──────────────────────────────┘Code language: CSS (css)
关键语法点
group … by … into g:将连接后的扁平结果按指定键重新分组,每组内可访问该学生的所有匹配行let:定义中间变量,避免在select中重复书写复杂表达式string.Join():将集合中的元素拼接为单个字符串,是实现”多行合一”的核心方法
与左连接的对比
| 场景 | 上一节(左连接) | 本节(分组聚合) |
|---|---|---|
| 连接方式 | join … into + DefaultIfEmpty() | 直接 join |
| 未匹配学生 | 显示,右表字段为 null | 不显示(内连接特性) |
| 匹配学生 | 每个学生多条记录 | 每个学生仅一行 |
| 产品展示 | 逐行列出 | 合并为一个字符串 |
如果需要保留未匹配学生,同时合并产品信息,可以将两种方式结合:先左连接,再分组聚合。
Previous: 理解左关联(Left Join)
Next: linq中的子查询