如何基于非主键ROBAID合并存在重复值的两个DataTable?
处理ROBAID重复时的DataTable合并方案
原来的Merge方法之所以失效,核心问题在于它依赖主键进行匹配更新——当dt2的ROBAID出现重复值时,你甚至没法再把它设为主键(主键要求唯一性),就算跳过主键设置,Merge也会把同ROBAID的行当成对同一记录的更新,而不是保留所有匹配行。针对你的需求,我提供两种可靠的实现方式:
方法一:手动遍历构建合并表
这种方式直观易控,适合需要自定义逻辑的场景:
// 1. 创建结果表,复制dt1的结构并添加需要的列 DataTable mergedTable = dt1.Clone(); mergedTable.Columns.Add("STANJE", dt2.Columns["STANJE"].DataType); mergedTable.Columns.Add("KOLICINA", dt2.Columns["KOLICINA"].DataType); // 2. 遍历dt1的每一行 foreach (DataRow row1 in dt1.Rows) { // 3. 筛选dt2中所有ROBAID匹配的行 string filter = $"ROBAID = {row1["ROBAID"]}"; // 如果ROBAID是字符串类型,要改成:filter = $"ROBAID = '{row1["ROBAID"]}'"; DataRow[] matchingRows = dt2.Select(filter); if (matchingRows.Length > 0) { // 4. 为每个匹配行创建合并后的新行 foreach (DataRow row2 in matchingRows) { DataRow newRow = mergedTable.NewRow(); // 复制dt1的所有列数据 foreach (DataColumn col in dt1.Columns) { newRow[col.ColumnName] = row1[col.ColumnName]; } // 填充dt2的列数据 newRow["STANJE"] = row2["STANJE"]; newRow["KOLICINA"] = row2["KOLICINA"]; mergedTable.Rows.Add(newRow); } } else { // 5. 无匹配行时,添加默认空值行 DataRow newRow = mergedTable.NewRow(); foreach (DataColumn col in dt1.Columns) { newRow[col.ColumnName] = row1[col.ColumnName]; } newRow["STANJE"] = DBNull.Value; newRow["KOLICINA"] = DBNull.Value; mergedTable.Rows.Add(newRow); } }
方法二:使用LINQ进行左连接查询
如果你习惯LINQ的写法,这种方式更简洁:
// 将DataTable转为可查询的集合 var dt1Enumerable = dt1.AsEnumerable(); var dt2Enumerable = dt2.AsEnumerable(); // 执行左连接,保留dt1的所有行,并匹配dt2中所有同ROBAID的行 var mergedData = from row1 in dt1Enumerable join row2 in dt2Enumerable on row1.Field<int>("ROBAID") equals row2.Field<int>("ROBAID") into groupedMatches from matchRow in groupedMatches.DefaultIfEmpty() select new { ROBAID = row1.Field<int>("ROBAID"), NAZIV = row1.Field<string>("NAZIV"), STANJE = matchRow != null ? matchRow.Field<int>("STANJE") : (int?)null, KOLICINA = matchRow != null ? matchRow.Field<int>("KOLICINA") : (int?)null }; // 将LINQ查询结果转为DataTable DataTable mergedTable = new DataTable(); mergedTable.Columns.Add("ROBAID", typeof(int)); mergedTable.Columns.Add("NAZIV", typeof(string)); mergedTable.Columns.Add("STANJE", typeof(int)); mergedTable.Columns.Add("KOLICINA", typeof(int)); foreach (var item in mergedData) { DataRow newRow = mergedTable.NewRow(); newRow["ROBAID"] = item.ROBAID; newRow["NAZIV"] = item.NAZIV; newRow["STANJE"] = item.STANJE ?? DBNull.Value; newRow["KOLICINA"] = item.KOLICINA ?? DBNull.Value; mergedTable.Rows.Add(newRow); }
关键注意点
- 如果ROBAID是字符串类型,要修改代码中的
Field<int>为Field<string>,同时手动筛选时要给值加单引号(简单场景下直接拼接即可,若需避免注入风险也可使用参数化逻辑)。 - 两种方法都会保留dt1的所有行,对于dt2中无匹配的ROBAID,STANJE和KOLICINA会设为
DBNull.Value,你可以根据需求改成默认值(比如0)。
内容的提问来源于stack exchange,提问作者Aleksa Ristic
相关产品推荐
相关产品推荐

