C#多个DataTable根据某一列匹配,其余字段相加求和的高效算法。

本文介绍了一种用于在数据环境中高效合并多个DataTable的算法,尤其适用于需要对比不同月份数据的场景。该算法通过优化循环和匹配过程,实现了快速的数据汇总,对于1000行数据和12张表的合并,耗时仅约0.2秒。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接到一个需求,在数据环境中,需要做出按1-12,可以选择 今年与去年两年 任意月度的数据进行对比。但是,这些数据又不存在于数据库中,不能直接求和、原数据 就在datatable中保存。所以就需要有多个DataTable相加的办法。但是,用datatable一个个循环加,效率不可恭维。绞尽脑汁想出一个办法,整个循环一次,就将最终的Table取出。

具体功能效果、大致如下

根据ID匹配  ID相同的行,再将 col1  和 col2 对应相加,得到总的table。

1、算法描述

这边是根据用户选中月份来统计,所以讲一下大致的思路。

将匹配的id从小到大排好序,并且知道用户选中的几个月。

然后 写一个循环,在循环中,同时循环这几个月份,并记录初始行号,并获取到id的最小值。

若是,匹配到ID同样是最小的,则将这两行的字段相加。

这样,每次循环都获取到当前id最小的行。加入新的tabel。

然后将之前的记录的循环行号+1。

那么第二次循环, 这些+1的就从第二行开始循环,依次类推,匹配到就+1。

若是行号,超出了当前月份最大表,就在循环中剔除,再次循环就跳过这张表。

直到所有表都被剔除时,结束,输出。

文字描述总是难以理解,来看一下 伪代码。

        public DataTable MergeDataTableByMonth1(Dictionary<int, DataTable> dicTable, DataTable dtBack, string year)
        {
            //这个monthYear存的是当前年度,用户选中的月份、
            List<int> monthYear = month[year];
            //用来储存月份及对应的行号  超过即移除,初始都为0  key为我们的月份。
            Dictionary<int, int> count = new Dictionary<int, int>();
            //循环count的数量,count里面有值说明还需要循环
            while (count.Count() > 0)
            {
                //记录DataRow合计值
                DataRow drInsert = dtBack.NewRow();
                //记录最小的id
                decimal smallId = 0;
                //记录最小id匹配需要updata的key
                List<int> UpdateKey = new List<int>();
                //记录超过table行上限需要remove的key
                List<int> RemoveKey = new List<int>();

                //循环每个月 获取 最小code的合计值 
                foreach (int key in monthYear)
                {
                    //并且 记录  需要 更新的key 已经需要移除的 key
                }
                //标记列增加 1  代表下次循环从下一行开始
                foreach (int key in UpdateKey)
                {
                    count[key] += 1;
                }
                //删除列  除去操作 代表这些月份已经全部相加
                foreach (int key in RemoveKey)
                {
                    monthYear.Remove(key);
                    count.Remove(key);
                }
                //最后将合计值统计插入返回Table
                dtBack.ImportRow(drInsert);
            }
            return dtBack;
        }

 

总结,相对于每次都要去循环,然后再匹配ID,效率已经快了非常多,测试 1000行数据,12张表相加,耗时0.2s左右,当然肯定还有更优的办法,希望大佬提供帮助。上述内容纯属自学。

注意,前提,排序好很重要。

 

最后附上实现代码!

villagecode即为我的ID,不是数字类型但也排好序了。前提!!排序!!增加了各种情况的判断。当然要用肯定还得自己修改。。

 public DataTable MergeDataTableByMonth(Dictionary<int, DataTable> dicTable, DataTable dtBack, string year)
        {
            List<int> monthYear = month[year];
            //用来储存月份及对应的行号  超过即移除
            Dictionary<int, int> count = new Dictionary<int, int>();
            foreach (int key in monthYear)
            {
                count.Add(key, 0);
            }

            if (dicTable.Count() > 0)
            {
                while (count.Count() > 0)
                {
                    //记录DataRow合计值
                    DataRow drInsert = dtBack.NewRow();
                    //记录最小的code
                    decimal smallCode = 0;
                    //updata的key
                    List<int> UpdateKey = new List<int>();
                    //remove的key
                    List<int> RemoveKey = new List<int>();

                    //循环每个月 获取 最小code的合计值 
                    foreach (int key in monthYear)
                    {
                        DataTable dt = dicTable[key];
                        if (dt.Rows.Count > count[key])
                        {
                            DataRow dr = dtBack.NewRow();
                            //循环到 第一个月份 code不为0的情况 
                            while (dr["villagecode"].ToDecimal() == 0)
                            {
                                if (dt.Rows.Count > count[key])
                                {
                                    dr = dt.Rows[count[key]];
                                    if (dr["villagecode"].ToDecimal() != 0)
                                    {
                                        //第一个月
                                        if (smallCode == 0)
                                        {
                                            smallCode = dr["villagecode"].ToDecimal();
                                            drInsert = dr;
                                            UpdateKey.Add(key);

                                        }
                                        //其余 月份 对比  组织较小
                                        else if (dr["villagecode"].ToDecimal() < smallCode)
                                        {
                                            drInsert = dr;
                                            UpdateKey.Clear();
                                            UpdateKey.Add(key);
                                        }
                                        else if (dr["villagecode"].ToDecimal() == smallCode)
                                        {
                                            drInsert = MargeDataRow(drInsert, dr);
                                            UpdateKey.Add(key);
                                        }
                                    }
                                    else
                                    {
                                        count[key] += 1;
                                    }
                                }
                                else
                                {
                                    RemoveKey.Add(key);
                                    break;
                                }
                            }

                        }
                        else
                        {
                            RemoveKey.Add(key);
                        }
                    }
                    //标记列增加 1
                    foreach (int key in UpdateKey)
                    {
                        count[key] += 1;
                    }
                    //删除列除去
                    foreach (int key in RemoveKey)
                    {
                        monthYear.Remove(key);
                        count.Remove(key);
                    }
                    dtBack.ImportRow(drInsert);
                }

            }

            return dtBack;
        }

 

<think>我们要在C#中使用DataTable实现:根据一列筛选数据,然后对另一列进行求和。有两种主要方式:1.使用DataTable的Compute方法2.使用LINQtoDataSet首先,我们假设有以下场景:我们有一个DataTable,其中包含多列数据,比如有一个“Category”列和一个“Amount”列。我们想根据“Category”列进行筛选(例如,只取Category为"Food"的行),然后对“Amount”列求和。方法一:使用DataTable.Compute方法步骤:a)使用DataTable.Select方法获取筛选后的行(或者直接使用Compute时在表达式中包含筛选条件)b)或者直接使用Compute方法,在表达式中同时指定筛选条件和求和函数。注意:Compute方法可以一次计算,表达式可以是聚合函数(如Sum)并包含条件。示例表达式:Sum(Amount)","Category='Food'"方法二:使用LINQ(需要引用System.Linq和System.Data.DataSetExtensions)步骤:a)使用AsEnumerable()将DataTable转换为IEnumerable<DataRow>b)使用Where方法进行筛选c)使用Sum方法求和下面分别给出示例:假设DataTable结构如下:列:Category(string),Amount(int)示例一(Compute方法):stringfilter="Category='Food'";//注意:如果Amount列是整型,则可以直接求和。如果是其他类型,需要转换。objectresult=dataTable.Compute("Sum(Amount)",filter);如果结果不是null,则转换为整数或小数等。示例二(LINQ方法):首先确保有引用:usingSystem.Linq;和usingSystem.Data;代码://假设DataTable名为dt//筛选Category为"Food"的行,然后对Amount列求和decimalsum=dt.AsEnumerable().Where(row=>row.Field<string>("Category")=="Food").Sum(row=>row.Field<decimal>("Amount"));注意:字段的数据类型要与实际匹配。如果Amount列是整数类型,可以使用Field<int>,如果是小数,则用decimal等。另外,如果我们在同一个操作中既需要筛选又需要求和,使用LINQ通常更直观。但是,用户提到要根据一列筛选(该列可能是任意列,并且条件可能是多个),然后对另一列求和。我们可以写一个通用的方法:方法一(Compute)的通用性:可以动态构建筛选条件字符串和表达式字符串。方法二(LINQ)的通用性:可以使用动态构建的谓词,但需要动态处理字段类型,这比较复杂。不过如果列名和条件固定,则直接写即可。下面我们给出两种方法的简单示例:示例代码:方法一:ComputestringcolumnToFilter="Category";//要筛选的列名stringfilterValue="Food";//筛选的值stringcolumnToSum="Amount";//要求和的列名stringfilterExpression=$"{columnToFilter}='{filterValue}'";objectresult=dataTable.Compute($"Sum({columnToSum})",filterExpression);//注意:这里假设筛选列是字符串类型,并且要求和的列是数值类型。如果筛选列不是字符串,可能需要更改单引号内的内容(比如数值就不需要单引号)。//如果有多条件,可以用and/or连接。方法二:LINQ(针对已知类型)stringcolumnToFilter="Category";stringfilterValue="Food";stringcolumnToSum="Amount";//注意:这里假设已知列的类型,实际中可能需要根据列类型进行动态处理varfilteredRows=dataTable.AsEnumerable().Where(row=>row.Field<string>(columnToFilter)==filterValue);decimalsum=filteredRows.Sum(row=>row.Field<decimal>(columnToSum));但是如果列类型是动态的,我们可以这样处理(使用Convert.ChangeType?):但LINQ表达式中的类型需要明确,所以动态类型处理比较复杂。因此,如果列类型固定,最好用上面的方法。考虑到简单性和通用性,如果条件简单,使用Compute方法更方便。注意:Compute方法在处理大型DataTable时性能可能不如LINQ,因为每次计算都需要解析表达式,并且筛选过程可能不是最优。另外,如果我们需要多次使用同一组筛选行,那么先用Select选出这些行,再用Compute或LINQ求和,则效率不高。此时,可以先筛选出行,再对这些行进行求和(LINQ更适合)。综上所述,根据需求选择合适的方法。下面给出一个完整的示例(使用Compute和LINQ两种方法):</think> 在C#中使用DataTable进行数据筛选和列值求和,有以下几种实现方式: ### 📌 方法一:使用LINQ(推荐方式) ```csharp // 假设按"Category"列筛选,对"Amount"列求和 string filterValue = "Electronics"; // 要筛选的值 string groupColumn = "Category"; // 分组列名 string sumColumn = "Amount"; // 求和列名 var resultSum = dt.AsEnumerable() .Where(row => row.Field<string>(groupColumn) == filterValue) .Sum(row => row.Field<int>(sumColumn)); Console.WriteLine($"分类为{filterValue}的总金额:{resultSum}"); ``` **说明**: 1. `AsEnumerable()`将DataTable转换为可查询集合 2. `Where()`实现指定列的筛选条件 3. `Sum()`对目标列进行聚合计算 4. 支持任何实现了`IComparable`的数据类型[^2][^3] ### ⚙ 方法二:使用Compute方法 ```csharp // 筛选条件 + 求和计算 string filterCondition = "Category = 'Electronics'"; object sumResult = dt.Compute($"SUM(Amount)", filterCondition); if (sumResult != DBNull.Value) { Console.WriteLine($"合计值:{Convert.ToDecimal(sumResult)}"); } ``` **特点**: - 单行代码完成筛选和求和 - 支持类似SQL的表达式语法 - 返回值需进行类型转换[^3][^4] ### 📊 方法三:分组后求和(多条件汇总) ```csharp // 按多列分组后求和(示例:按品名+规格分组,统计数量总和) var groupedData = dt.AsEnumerable() .GroupBy(r => new { Name = r.Field<string>("品名"), Spec = r.Field<string>("规格") }) .Select(g => new { g.Key.Name, g.Key.Spec, Total = g.Sum(r => r.Field<int>("数量")) }); foreach(var item in groupedData) { Console.WriteLine($"{item.Name}({item.Spec}): {item.Total}"); } ``` **应用场景**: - 多维度数据汇总 - 分组统计报表 - 交叉数据分析[^1][^2] ### 🔍 方法四:Select方法筛选(早期.NET版本) ```csharp // 适用于.NET Framework 2.0+ DataRow[] filteredRows = dt.Select("Category = 'Electronics'"); decimal sum = 0; foreach(DataRow row in filteredRows) { sum += Convert.ToDecimal(row["Amount"]); } ``` 💡 **性能建议**: 1. 大数据集 (>10,000行) 优先使用LINQ,性能更优 2. 简单筛选求和用Compute更简洁 3. 对日期类型字段需注意格式转换 --- **注意事项**: 1. 求和列需为数值类型(int/decimal/double等) 2. 空值处理建议添加默认值:`row.Field<int?>("列名") ?? 0` 3. 列名大小写敏感 4. 复杂条件可使用组合表达式: ```csharp .Where(row => row.Field<string>("Category") == "Electronics" && row.Field<DateTime>("Date") > new DateTime(2023,1,1)) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值