You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用C#通过LINQ实现基于动态数据的透视表

C# 实现DataTable行转列(透视表)

原始数据表

员工操作值表

idEmployeeopidopvalue
1A111
1A212
2B212
3C111
3C212
3C313
............
nN111
nN212

操作名称映射表

idopName
1opA
2opB
......
nopN

目标数据表结构

idEmployeeopAopBopC...opN
1A1112null...
2Bnull12null...
3C111213...
.....................
nN1112null...

实现代码

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));
        }
    }
}

关键说明

  1. 映射字典:通过Linq将操作名称表转换为字典,快速根据opid获取对应的列名
  2. 动态列创建:遍历映射字典,为目标表添加所有操作名称列,设置允许空值
  3. 分组填充:按员工id和姓名分组,每组对应目标表的一行,填充该员工的所有操作值,未涉及的操作列自动留空(DBNull)
  4. 异常防护:使用TryGetValue避免原始数据中出现未定义的opid导致报错

内容的提问来源于stack exchange,提问作者Borhan Yazdani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 22:25:56