C#高效对比两个DataTable,获取新增/更新/删除行
高效对比大DataTable(新增/更新/删除行)的LINQ实现方案
针对两个10万+行的DataTable(今日/昨日表,均含唯一列Master_ID),直接逐行全量遍历性能极差。以下是基于LINQ+字典映射的高效实现方案,核心思路是通过Master_ID构建哈希字典,将查找操作的时间复杂度从O(n)降至O(1):
实现步骤
- 为两张表分别构建
Master_ID到DataRow的字典映射,避免重复遍历查找 - 筛选新增行:今日表中
Master_ID不存在于昨日表的行 - 筛选删除行:昨日表中
Master_ID不存在于今日表的行 - 筛选更新行:
Master_ID同时存在于两张表,但行内容存在差异的行
代码实现
首先定义行内容对比方法(DataRow默认Equals为引用比较,需自定义值对比逻辑):
private static bool AreRowsEqual(DataRow row1, DataRow row2) { foreach (DataColumn col in row1.Table.Columns) { // 处理DBNull.Value的空值情况 var val1 = row1[col] == DBNull.Value ? null : row1[col]; var val2 = row2[col] == DBNull.Value ? null : row2[col]; if (!Equals(val1, val2)) { return false; } } return true; }
主对比逻辑:
public Dictionary<string, List<DataRow>> CompareLargeDataTables(DataTable todayTable, DataTable yesterdayTable) { // 构建昨日表的ID-行映射,一次遍历完成 var yesterdayRowMap = yesterdayTable.AsEnumerable() .ToDictionary(row => row["Master_ID"].ToString(), row => row); // 构建今日表的ID-行映射 var todayRowMap = todayTable.AsEnumerable() .ToDictionary(row => row["Master_ID"].ToString(), row => row); // 新增行:今日有,昨日无 var added = todayTable.AsEnumerable() .Where(row => !yesterdayRowMap.ContainsKey(row["Master_ID"].ToString())) .ToList(); // 删除行:昨日有,今日无 var deleted = yesterdayTable.AsEnumerable() .Where(row => !todayRowMap.ContainsKey(row["Master_ID"].ToString())) .ToList(); // 更新行:ID存在,但内容不同 var updated = todayTable.AsEnumerable() .Where(row => { var id = row["Master_ID"].ToString(); return yesterdayRowMap.TryGetValue(id, out var yesterdayRow) && !AreRowsEqual(row, yesterdayRow); }) .ToList(); return new Dictionary<string, List<DataRow>> { ["Added"] = added, ["Updated"] = updated, ["Deleted"] = deleted }; }
性能优化提示
- 强类型键优化:如果
Master_ID是int/long等值类型,直接用原类型作为字典键,避免字符串转换的性能开销 - 列范围缩小:如果不需要对比所有30列,可修改
AreRowsEqual方法只对比指定列,减少计算量 - 哈希预计算:对行内容预计算哈希值(如将列值组合成Tuple或拼接成唯一字符串后取HashCode),可进一步加快行对比速度,但需注意处理空值和类型冲突问题
内容的提问来源于stack exchange,提问作者Aashish
相关产品推荐
相关产品推荐

