1. 为什么需要排序与分组?

在数据处理中,排序(ORDER BY)和分组(GROUP BY)是最常用的两种数据整理方式。电商网站的商品价格筛选、学生成绩的班级排名、销售数据的区域统计等场景都需要这两种操作。在C#中通过MySqlConnector(官方推荐的MySQL .NET驱动)执行这类查询,既能保证执行效率,又能实现原生SQL的完整功能。

2. 环境准备与基础连接

2.1 安装MySqlConnector

通过NuGet包管理器安装最新稳定版:

Install-Package MySqlConnector -Version 2.3.0

2.2 建立数据库连接

using MySqlConnector;

// 创建连接字符串(根据实际情况修改参数)
var connectionString = "Server=localhost;User ID=root;Password=123456;Database=SchoolDB";

// 创建连接对象
using var connection = new MySqlConnection(connectionString);

try 
{
    await connection.OpenAsync();
    Console.WriteLine("数据库连接成功!");
}
catch (Exception ex)
{
    Console.WriteLine($"连接失败:{ex.Message}");
}

3. 数据排序实战

3.1 单字段基础排序

查询学生表并按年龄升序排列:

var sql = @"
    SELECT StudentName, Age, ClassName 
    FROM Students 
    ORDER BY Age ASC;  -- ASC表示升序,DESC表示降序";

using var command = new MySqlCommand(sql, connection);
using var reader = await command.ExecuteReaderAsync();

Console.WriteLine("姓名\t年龄\t班级");
while (await reader.ReadAsync())
{
    Console.WriteLine($"{reader["StudentName"]}\t{reader["Age"]}\t{reader["ClassName"]}");
}

3.2 多字段组合排序

当年龄相同时按姓名首字母排序:

var sql = @"
    SELECT StudentName, Score 
    FROM ExamResults 
    ORDER BY Score DESC, StudentName ASC;  -- 先按分数降序,再按姓名升序";

4. 数据分组深度应用

4.1 基础分组统计

统计每个班级的平均分:

var sql = @"
    SELECT ClassName, AVG(Score) AS AvgScore 
    FROM ExamResults 
    GROUP BY ClassName;";

// 执行查询后处理结果
while (await reader.ReadAsync())
{
    Console.WriteLine($"班级:{reader["ClassName"]},均分:{reader.GetDecimal("AvgScore"):F2}");
}

4.2 分组后筛选(HAVING)

筛选平均分高于80的班级:

var sql = @"
    SELECT ClassName, AVG(Score) AS AvgScore 
    FROM ExamResults 
    GROUP BY ClassName
    HAVING AvgScore > 80;";  -- HAVING用于分组后筛选

5. 关联技术:参数化查询

防止SQL注入的安全写法:

var minScore = 90;
var sql = @"
    SELECT StudentName 
    FROM ExamResults 
    WHERE Score > @scoreThreshold 
    ORDER BY Score DESC";

var command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@scoreThreshold", minScore);  // 参数化传值

6. 典型应用场景分析

6.1 动态排序场景

Web应用中根据用户选择的排序字段动态生成SQL:

string sortField = Request.Query["sortBy"];  // 从前端获取排序字段
var validColumns = new HashSet<string> { "Score", "Age", "BirthDate" };

if (validColumns.Contains(sortField)) {
    sql += $" ORDER BY {sortField} DESC";
}

6.2 多层分组报表

生成销售数据的区域-产品类型二级分组报表:

var sql = @"
    SELECT Region, ProductType, SUM(SalesAmount)
    FROM SalesRecords
    GROUP BY Region, ProductType
    ORDER BY Region ASC, SUM(SalesAmount) DESC";

7. 技术方案优缺点

优势特性:

  • 原生SQL支持:可直接使用MySQL特有语法(如WITH ROLLUP)
  • 高性能:基准测试显示比Entity Framework快3倍以上
  • 轻量化:安装包仅200KB,适合容器化部署

需要改进:

  • 缺乏LINQ支持:需手动编写SQL语句
  • 异步超时设置:默认15秒需根据场景调整
var command = new MySqlCommand(sql, connection) 
{ 
    CommandTimeout = 30  // 设置超时时间为30秒
};

8. 关键注意事项

  1. 连接池管理:默认开启连接池,建议保持开启状态
  2. 数据类型映射:MySQL的UNSIGNED INT需映射为C#的ulong类型
  3. 索引优化:对GROUP BY字段建议创建组合索引
CREATE INDEX idx_group ON SalesRecords (Region, ProductType);

9. 开发调试技巧

启用详细日志记录:

var connectionString = "Server=...;Logging=True;";

在开发环境查看实际执行的SQL:

// 输出带参数的完整SQL
Console.WriteLine(command.CommandText);
foreach (MySqlParameter p in command.Parameters)
{
    Console.WriteLine($"{p.ParameterName} = {p.Value}");
}

10. 总结与选择建议

对于需要直接控制SQL、追求极致性能的C#项目,MySqlConnector是连接MySQL的最佳选择。在涉及复杂分组(如分组后排序TOP N记录)的场景中,建议在数据库层完成计算,相比在C#中处理内存数据,效率可提升10倍以上。