在C#开发中处理数据库数据的时候,经常需要对数据做分组和聚合操作,比如统计每个分类下的商品数量、计算每个部门的平均薪资等。除了直接编写SQL语句执行之外,使用LINQ可以更简洁地实现这类需求,不用手动拼接复杂的SQL字符串。

一、原生SQL方式实现分组聚合
如果是直接使用ADO.NET操作数据库,我们可以通过拼接SQL语句的方式完成分组聚合,这种方式兼容性更好,适合对SQL比较熟悉的开发者。下面是一个查询用户表中每个城市用户数量的示例:
using System;
using System.Data;
using System.Data.SqlClient;
class Program
{
static void Main()
{
string connectionString = "Server=127.0.0.1;Database=TestDB;Uid=sa;Pwd=123456;";
string sql = "SELECT City, COUNT(*) AS UserCount FROM Users GROUP BY City";
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
adapter.Fill(dt);
foreach (DataRow row in dt.Rows)
{
Console.WriteLine($"城市:{row["City"]},用户数量:{row["UserCount"]}");
}
}
}
}
二、使用LINQ实现分组聚合
LINQ提供了更接近自然语言的查询语法,我们可以在获取到数据集合之后,使用group by子句完成分组,再搭配聚合函数计算对应结果。如果是操作EF Core等ORM框架,LINQ查询还会自动转换为对应的SQL语句执行,减少手动编写SQL的工作量。
2.1 LINQ查询语法实现
下面是用LINQ查询语法实现上述统计每个城市用户数量的功能:
using System;
using System.Collections.Generic;
using System.Linq;
class User
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
}
class Program
{
static void Main()
{
// 模拟从数据库获取的用户集合
List<User> userList = new List<User>
{
new User { Id = 1, Name = "张三", City = "北京" },
new User { Id = 2, Name = "李四", City = "上海" },
new User { Id = 3, Name = "王五", City = "北京" },
new User { Id = 4, Name = "赵六", City = "广州" },
new User { Id = 5, Name = "钱七", City = "上海" }
};
// LINQ查询语法分组聚合
var cityUserCount = from user in userList
group user by user.City into g
select new
{
City = g.Key,
UserCount = g.Count()
};
foreach (var item in cityUserCount)
{
Console.WriteLine($"城市:{item.City},用户数量:{item.UserCount}");
}
}
}
2.2 LINQ方法语法实现
除了查询语法,LINQ还支持方法语法,通过GroupBy方法配合后续聚合操作实现相同的效果,写法更加灵活:
using System;
using System.Collections.Generic;
using System.Linq;
class User
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public decimal Salary { get; set; }
}
class Program
{
static void Main()
{
List<User> userList = new List<User>
{
new User { Id = 1, Name = "张三", City = "北京", Salary = 8000 },
new User { Id = 2, Name = "李四", City = "上海", Salary = 12000 },
new User { Id = 3, Name = "王五", City = "北京", Salary = 10000 },
new User { Id = 4, Name = "赵六", City = "广州", Salary = 9000 },
new User { Id = 5, Name = "钱七", City = "上海", Salary = 15000 }
};
// 方法语法:按城市分组,统计每个城市的平均薪资和用户数量
var cityStat = userList.GroupBy(u => u.City)
.Select(g => new
{
City = g.Key,
AvgSalary = g.Average(u => u.Salary),
UserCount = g.Count()
});
foreach (var item in cityStat)
{
Console.WriteLine($"城市:{item.City},平均薪资:{item.AvgSalary},用户数量:{item.UserCount}");
}
}
}
三、常用聚合函数说明
LINQ中提供了多种聚合函数,可以搭配分组操作使用,常见的聚合函数如下:
- Count():统计分组中的元素数量
- Sum(selector):计算分组中指定属性的总和
- Average(selector):计算分组中指定属性的平均值
- Max(selector):获取分组中指定属性的最大值
- Min(selector):获取分组中指定属性的最小值
四、两种实现方式的对比
| 实现方式 | 优势 | 劣势 |
|---|---|---|
| 原生SQL | 兼容性好,适合复杂SQL场景,性能可控 | 需要手动拼接字符串,容易出现SQL注入风险,代码可读性较差 |
| LINQ | 语法简洁,类型安全,自动转换SQL,可读性强 | 复杂场景下生成的SQL可能不够优化,需要了解LINQ的底层转换逻辑 |
实际开发中可以根据项目情况选择,如果是使用ORM框架优先推荐用LINQ,减少手动SQL的维护成本;如果是需要执行非常复杂的分组聚合逻辑,也可以考虑原生SQL实现。