如何使用C#通过LINQ实现基于动态数据的透视表
C# 实现DataTable行转列(透视表)
原始数据表
员工操作值表
| id | Employee | opid | opvalue |
|---|---|---|---|
| 1 | A | 1 | 11 |
| 1 | A | 2 | 12 |
| 2 | B | 2 | 12 |
| 3 | C | 1 | 11 |
| 3 | C | 2 | 12 |
| 3 | C | 3 | 13 |
| ... | ... | ... | ... |
| n | N | 1 | 11 |
| n | N | 2 | 12 |
操作名称映射表
| id | opName |
|---|---|
| 1 | opA |
| 2 | opB |
| ... | ... |
| n | opN |
目标数据表结构
| id | Employee | opA | opB | opC | ... | opN |
|---|---|---|---|---|---|---|
| 1 | A | 11 | 12 | null | ... | |
| 2 | B | null | 12 | null | ... | |
| 3 | C | 11 | 12 | 13 | ... | |
| ... | ... | ... | ... | ... | ... | ... |
| n | N | 11 | 12 | null | ... |
实现代码
using System; using System.Data; using System.Linq; class Program { static void Main() { // 模拟原始员工操作数据表 DataTable employeeOps = new DataTable(); employeeOps.Columns.Add("id", typeof(int)); employeeOps.Columns.Add("Employee", typeof(string)); employeeOps.Columns.Add("opid", typeof(int)); employeeOps.Columns.Add("opvalue", typeof(int)); // 填充示例数据 employeeOps.Rows.Add(1, "A", 1, 11); employeeOps.Rows.Add(1, "A", 2, 12); employeeOps.Rows.Add(2, "B", 2, 12); employeeOps.Rows.Add(3, "C", 1, 11); employeeOps.Rows.Add(3, "C", 2, 12); employeeOps.Rows.Add(3, "C", 3, 13); employeeOps.Rows.Add(4, "N", 1, 11); employeeOps.Rows.Add(4, "N", 2, 12); // 模拟操作名称映射表 DataTable opNames = new DataTable(); opNames.Columns.Add("id", typeof(int)); opNames.Columns.Add("opName", typeof(string)); opNames.Rows.Add(1, "opA"); opNames.Rows.Add(2, "opB"); opNames.Rows.Add(3, "opC"); opNames.Rows.Add(4, "opN"); // 构建opid到opName的映射字典 var opMapping = opNames.AsEnumerable() .ToDictionary(row => row.Field<int>("id"), row => row.Field<string>("opName")); // 创建目标DataTable,添加固定列 DataTable targetTable = new DataTable(); targetTable.Columns.Add("id", typeof(int)); targetTable.Columns.Add("Employee", typeof(string)); // 动态添加所有操作名称列,允许空值 foreach (var opName in opMapping.Values) { targetTable.Columns.Add(opName, typeof(int)).AllowDBNull = true; } // 按id和Employee分组,填充目标表数据 var groupedData = employeeOps.AsEnumerable() .GroupBy(row => new { Id = row.Field<int>("id"), Emp = row.Field<string>("Employee") }); foreach (var group in groupedData) { DataRow newRow = targetTable.NewRow(); newRow["id"] = group.Key.Id; newRow["Employee"] = group.Key.Emp; foreach (var row in group) { int opId = row.Field<int>("opid"); // 避免opid不在映射表中的情况 if (opMapping.TryGetValue(opId, out var opName)) { newRow[opName] = row.Field<int>("opvalue"); } } targetTable.Rows.Add(newRow); } // 验证输出(可移除) foreach (DataRow row in targetTable.Rows) { Console.WriteLine(string.Join("\t", row.ItemArray)); } } }
关键说明
- 映射字典:通过Linq将操作名称表转换为字典,快速根据opid获取对应的列名
- 动态列创建:遍历映射字典,为目标表添加所有操作名称列,设置允许空值
- 分组填充:按员工id和姓名分组,每组对应目标表的一行,填充该员工的所有操作值,未涉及的操作列自动留空(DBNull)
- 异常防护:使用
TryGetValue避免原始数据中出现未定义的opid导致报错
内容的提问来源于stack exchange,提问作者Borhan Yazdani
相关产品推荐
相关产品推荐

