C#中如何高效合并主键不同的一对多DataTable?
高效实现DataTable内连接(INNER JOIN)的方案
针对10k-15k行的DataTable,LINQ循环赋值效率低的问题,推荐以下两种高效方案:
方案一:哈希表缓存+遍历匹配(最优性能)
利用哈希表O(1)的查找特性,先缓存dt1的主键映射,再遍历dt2快速匹配,整体时间复杂度为O(M+N),适合大数据量场景。
// 1. 构建dt1的哈希缓存,Key为Code值,Value为对应DataRow var dt1Cache = new Dictionary<object, DataRow>(); foreach (DataRow row in dt1.Rows) { object codeKey = row["Code"]; if (!dt1Cache.ContainsKey(codeKey)) { dt1Cache.Add(codeKey, row); } } // 2. 创建结果DataTable,合并dt1与dt2的列结构(处理列名冲突) DataTable resultDt = dt1.Clone(); foreach (DataColumn col in dt2.Columns) { string targetColName = col.ColumnName; // 若列名重复,给dt2的列加前缀区分 if (resultDt.Columns.Contains(targetColName)) { targetColName = $"dt2_{targetColName}"; } resultDt.Columns.Add(targetColName, col.DataType); } // 3. 遍历dt2,匹配dt1数据并插入结果表 foreach (DataRow dt2Row in dt2.Rows) { object fCode = dt2Row["F_Code"]; if (dt1Cache.TryGetValue(fCode, out DataRow dt1Row)) { DataRow newRow = resultDt.NewRow(); // 复制dt1的列数据 foreach (DataColumn col in dt1.Columns) { newRow[col.ColumnName] = dt1Row[col.ColumnName]; } // 复制dt2的列数据 foreach (DataColumn col in dt2.Columns) { string targetColName = resultDt.Columns.Contains(col.ColumnName) ? $"dt2_{col.ColumnName}" : col.ColumnName; newRow[targetColName] = dt2Row[col.ColumnName]; } resultDt.Rows.Add(newRow); } }
方案二:DataSet+DataRelation(代码更规整)
借助ADO.NET内置的DataRelation关联两张表,通过父/子行获取匹配数据,性能略逊于哈希表方案,但代码更简洁易维护。
// 1. 将两张表加入DataSet DataSet ds = new DataSet(); ds.Tables.Add(dt1.Copy()); ds.Tables.Add(dt2.Copy()); // 2. 定义关联关系:dt1.Code <-> dt2.F_Code DataRelation joinRelation = new DataRelation( "Dt1_Dt2_Join", ds.Tables[0].Columns["Code"], ds.Tables[1].Columns["F_Code"]); ds.Relations.Add(joinRelation); // 3. 创建结果表结构(同方案一,处理列名冲突) DataTable resultDt = dt1.Clone(); foreach (DataColumn col in dt2.Columns) { string targetColName = col.ColumnName; if (resultDt.Columns.Contains(targetColName)) { targetColName = $"dt2_{targetColName}"; } resultDt.Columns.Add(targetColName, col.DataType); } // 4. 遍历dt2,通过关联关系获取匹配的dt1行并合并 foreach (DataRow dt2Row in ds.Tables[1].Rows) { DataRow[] matchedDt1Rows = dt2Row.GetParentRows(joinRelation); if (matchedDt1Rows.Length > 0) { DataRow dt1Row = matchedDt1Rows[0]; DataRow newRow = resultDt.NewRow(); // 复制dt1数据 foreach (DataColumn col in dt1.Columns) { newRow[col.ColumnName] = dt1Row[col.ColumnName]; } // 复制dt2数据 foreach (DataColumn col in dt2.Columns) { string targetColName = resultDt.Columns.Contains(col.ColumnName) ? $"dt2_{col.ColumnName}" : col.ColumnName; newRow[targetColName] = dt2Row[col.ColumnName]; } resultDt.Rows.Add(newRow); } }
注意事项
- 确保dt1的
Code与dt2的F_Code数据类型完全一致,否则匹配会失败。 - 列名冲突必须处理,否则创建结果表时会抛出异常,示例中通过添加
dt2_前缀解决。 - dt1的
Code作为主键,确保无重复值,避免哈希缓存出现覆盖问题。
内容的提问来源于stack exchange,提问作者Atrus2711
相关产品推荐
相关产品推荐

