-
LINQ高级查询——C#进行复杂数据分组与聚合
第一部分:C#基础入门
LINQ高级查询——复杂数据分组与聚合
实例介绍
在处理电商订单分析、用户行为统计等复杂数据场景时,传统的循环+字典统计方式不仅代码冗长,还容易出错。LINQ的高级查询(分组、聚合)通过声明式语法,能快速实现复杂数据统计,比如按用户分组统计消费总额、按年月分组分析销售趋势等。本节以电商订单数据分析为场景,展示LINQ在分组、聚合、嵌套分组等高级场景的应用,让你明白“为什么LINQ能vb.net教程C#教程python教程SQL教程access 2010教程
让数据处理事半功倍”。
需求分析
设计电商订单数据分析工具,核心需求如下:
1.用户消费统计:按用户分组,统计每个用户的订单总数、总消费金额、平均订单金额;
2.销售趋势分析:按年月分组,统计每月的销售总额、订单数;
3.嵌套分组统计:按用户+商品类别嵌套分组,统计每个用户在不同类别下的消费情况;
4.过滤分组结果:只显示订单数≥3的用户;
5.多条件分组:按用户+订单状态分组,统计每个用户不同状态的订单数。
代码实现
-
核心模型类
csharp
using System;
using System.Collections.Generic;
using System.Linq;
// 用户模型
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
// 商品模型
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; } // 商品类别:电子、服装、食品等
public decimal Price { get; set; }
}
// 订单模型
public class Order
{
public int Id { get; set; }
public int UserId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; } // 购买数量
public DateTime OrderDate { get; set; }
public string Status { get; set; } // 订单状态:已支付、已发货、已完成
}
-
模拟数据构造
csharp
// 模拟数据
public static class DataGenerator
{
public static List<User> GetUsers()
{
return new List<User>
{
new User { Id = 1, Name = "张三" },
new User { Id = 2, Name = "李四" },
new User { Id = 3, Name = "王五" }
};
}
public static List<Product> GetProducts()
{
return new List<Product>
{
new Product { Id = 1, Name = "笔记本电脑", Category = "电子", Price = 5999.99m },
new Product { Id = 2, Name = "T恤", Category = "服装", Price = 99.99m },
new Product { Id = 3, Name = "牛奶", Category = "食品", Price = 59.99m },
new Product { Id = 4, Name = "手机", Category = "电子", Price = 3999.99m }
};
}
public static List<Order> GetOrders()
{
var random = new Random();
return new List<Order>
{
new Order { Id = 1, UserId = 1, ProductId = 1, Quantity = 1, OrderDate = new DateTime(2024, 1, 15), Status = "已完成" },
new Order { Id = 2, UserId = 1, ProductId = 2, Quantity = 2, OrderDate = new DateTime(2024, 1, 20), Status = "已完成" },
new Order { Id = 3, UserId = 1, ProductId = 3, Quantity = 3, OrderDate = new DateTime(2024, 2, 5), Status = "已完成" },
new Order { Id = 4, UserId = 2, ProductId = 4, Quantity = 1, OrderDate = new DateTime(2024, 1, 10), Status = "已完成" },
new Order { Id = 5, UserId = 2, ProductId = 2, Quantity = 1, OrderDate = new DateTime(2024, 2, 10), Status = "已发货" },
new Order { Id = 6, UserId = 3, ProductId = 3, Quantity = 5, OrderDate = new DateTime(2024, 1, 25), Status = "已完成" },
new Order { Id = 7, UserId = 3, ProductId = 1, Quantity = 1, OrderDate = new DateTime(2024, 2, 15), Status = "已支付" },
new Order { Id = 8, UserId = 3, ProductId = 4, Quantity = 1, OrderDate = new DateTime(2024, 2, 20), Status = "已完成" },
new Order { Id = 9, UserId = 3, ProductId = 2, Quantity = 3, OrderDate = new DateTime(2024, 3, 1), Status = "已完成" }
};
}
}
-
LINQ高级查询示例
csharp
class Program
{
static void Main(string[] args)
{
var users = DataGenerator.GetUsers();
var products = DataGenerator.GetProducts();
var orders = DataGenerator.GetOrders();
Console.WriteLine("=== 1. 用户消费统计(按用户分组) ===");
// 按用户分组,统计订单数、总金额、平均金额
var userStats = from order in orders
join product in products on order.ProductId equals product.Id
group new { order, product } by order.UserId into userGroup
join user in users on userGroup.Key equals user.Id
select new
{
UserName = user.Name,
OrderCount = userGroup.Count(),
TotalAmount = userGroup.Sum(item => item.order.Quantity * item.product.Price),
AvgAmount = userGroup.Average(item => item.order.Quantity * item.product.Price)
};
foreach (var stat in userStats)
{
Console.WriteLine($"用户:{stat.UserName},订单数:{stat.OrderCount},总金额:{stat.TotalAmount:C},平均金额:{stat.AvgAmount:C}");
}
Console.WriteLine("
=== 2. 销售趋势分析(按年月分组) ===");
// 按年月分组,统计销售总额、订单数
var monthlyStats = from order in orders
join product in products on order.ProductId equals product.Id
group new { order, product } by new { order.OrderDate.Year, order.OrderDate.Month } into monthGroup
select new
{
Year = monthGroup.Key.Year,
Month = monthGroup.Key.Month,
TotalSales = monthGroup.Sum(item => item.order.Quantity * item.product.Price),
OrderCount = monthGroup.Count()
};
foreach (var stat in monthlyStats.OrderBy(s => s.Year).ThenBy(s => s.Month))
{
Console.WriteLine($"{stat.Year}年{stat.Month}月:销售总额{stat.TotalSales:C},订单数{stat.OrderCount}");
}
Console.WriteLine("
=== 3. 嵌套分组(用户+商品类别) ===");
// 先按用户分组,再按商品类别分组
var nestedGroup = from order in orders
join product in products on order.ProductId equals product.Id
group new { order, product } by order.UserId into userGroup
join user in users on userGroup.Key equals user.Id
select new
{
UserName = user.Name,
CategoryGroups = from item in userGroup
group item by item.product.Category into categoryGroup
select new
{
Category = categoryGroup.Key,
TotalAmount = categoryGroup.Sum(i => i.order.Quantity * i.product.Price),
OrderCount = categoryGroup.Count()
}
};
foreach (var userGroup in nestedGroup)
{
Console.WriteLine($"用户:{userGroup.UserName}");
foreach (var category in userGroup.CategoryGroups)
{
Console.WriteLine($" 类别:{category.Category},总金额:{category.TotalAmount:C},订单数:{category.OrderCount}");
}
}
Console.WriteLine("
=== 4. 过滤分组结果(订单数≥3的用户) ===");
// 分组后过滤(相当于SQL的HAVING)
var filteredUserStats = from order in orders
join product in products on order.ProductId equals product.Id
group new { order, product } by order.UserId into userGroup
where userGroup.Count() >=3 // 过滤订单数≥3的分组
join user in users on userGroup.Key equals user.Id
select new
{
UserName = user.Name,
OrderCount = userGroup.Count(),
TotalAmount = userGroup.Sum(item => item.order.Quantity * item.product.Price)
};
foreach (var stat in filteredUserStats)
{
Console.WriteLine($"用户:{stat.UserName},订单数:{stat.OrderCount},总金额:{stat.TotalAmount:C}");
}
Console.WriteLine("
=== 5. 多条件分组(用户+订单状态) ===");
// 按用户和订单状态分组
var multiKeyGroup = from order in orders
group order by new { order.UserId, order.Status } into multiGroup
join user in users on multiGroup.Key.UserId equals user.Id
select new
{
UserName = user.Name,
Status = multiGroup.Key.Status,
OrderCount = multiGroup.Count()
};
foreach (var stat in multiKeyGroup)
{
Console.WriteLine($"用户:{stat.UserName},状态:{stat.Status},订单数:{stat.OrderCount}");
}
}
}
逐行讲解
-
用户消费统计
join操作:关联订单和商品表,获取商品价格;
group by order.UserId:按用户ID分组,将订单和商品的联合数据分组到userGroup;
join user:关联用户表,获取用户名;
聚合函数:Count()统计订单数,Sum()计算总金额,Average()计算平均金额;
匿名类型:用匿名类型存储结果,简化代码。 -
销售趋势分析
多键分组:用new { Year, Month }作为分组键,实现按年月分组;
OrderBy/ThenBy:对结果按年月排序,使输出更直观;
Sum/Count:统计每月销售总额和订单数。 -
嵌套分组
两层分组:先按用户分组,再在用户分组内按商品类别分组;
CategoryGroups:内层分组结果,存储每个用户下不同类别的消费数据;
嵌套遍历:外层遍历用户,内层遍历类别,输出详细统计。 -
过滤分组结果
where子句:在分组后使用where,过滤出订单数≥3的用户(相当于SQL的HAVING子句);
执行顺序:先分组,再过滤,最后关联用户表获取用户名。 -
多条件分组
多键匿名类型:用new { UserId, Status }作为分组键,实现按用户和订单状态双重分组;
统计结果:显示每个用户不同状态的订单数,帮助分析订单处理情况。
基础知识拓展 -
LINQ分组的核心概念
IGrouping<TKey, TElement>:分组结果的类型,包含Key(分组键)和实现IEnumerable(分组内的元素);
延迟执行:LINQ查询在遍历结果时才执行,若数据变化,结果会随之变化;
匿名类型:用于多键分组或存储临时结果,编译器自动生成类,属性只读。 -
LINQ分组与SQL分组的区别
特性 LINQ分组 SQL分组
分组结果 保留分组内所有元素 只保留分组键和聚合结果
多键分组 用匿名类型 用逗号分隔多个列
嵌套分组 支持多层分组 需用子查询或CTE
过滤时机 分组前(where)或分组后(where) 分组前(WHERE)或分组后(HAVING) -
性能优化技巧
提前过滤:在分组前用where过滤不需要的数据,减少分组的数据量;
避免重复关联:若多次关联同一表,可先关联再分组;
使用索引:若数据来自数据库,确保分组键列有索引,提高查询速度;
ToList()/ToArray():若需多次使用分组结果,用ToList()缓存结果,避免重复执行查询。 -
常见聚合函数
Count():统计元素数量;
Sum():计算数值型元素的总和;
Average():计算数值型元素的平均值;
Max()/Min():获取元素的最大值/最小值;
First()/Last():获取分组内第一个/最后一个元素。
总结
LINQ高级查询通过分组、聚合、嵌套分组等功能,能快速处理复杂数据统计任务,代码简洁易读,比传统循环方式效率更高。掌握LINQ高级查询,能大幅提升数据处理能力,尤其在数据分析、报表生成等场景中应用广泛。核心要点:
用group by实现分组,into关键字保存分组结果;
多键分组用匿名类型,嵌套分组用内层查询;
聚合函数用于统计分组数据,where子句可过滤分组结果;
延迟执行需注意数据变化对结果的影响。
通过本节学习,你可以将LINQ高级查询应用到实际项目中,提高代码质量和开发效率。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49428.html
最新更新
表达式树——C#测试动态查询构建器
LINQ高级查询——C#进行复杂数据分组与聚
异步IO操作——C#编写关于大文件多线程下
异步编程基础——C#开发Task异步方法设计
事件驱动编程——C#开发实时消息通知系
委托与Lambda——C#简化事件处理逻辑
泛型集合自定义——C#关于类型安全的队
Excel操作——用C#开发学生成绩表生成(
CSV文件处理——C#编写销售数据导入导出
数据验证——用C#开发表单输入合法性检
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比










