Full outer join, on 2 data tables, with a list of columns
我有两个数据表,我不知道它们的数据列列表。此列表必须在运行时提取,并用于完整的外部联接。
使用这些列时,需要合并两个表之间的列,并且需要显示所有数据。
到现在为止我所做的是
但是,我在第二步遇到了与Linq有关的问题。
到目前为止,我有:
获取公用列
1 2 | // Get common columns var commonColumns = dt1.Columns.OfType().Intersect(dt2.Columns.OfType(), new DataColumnComparer()); |
创建新数据表
1 2 3 4 5 6 7 8 | // Create the result which is going to be sent to the user DataTable result = new DataTable(); // Add all the columns from both tables result.Columns.AddRange( dt1.Columns.OfType() .Union(dt2.Columns.OfType(), new DataColumnComparer()) .Select(c => new DataColumn(c.Caption, c.DataType, c.Expression, c.ColumnMapping)).ToArray()); |
如何从运行时提取的数据列列表中动态获取有效的完整外部联接?
这可能对你有用
1 2 3 4 5 6 7 | var commonColumns = dt1.Columns.OfType<DataColumn>().Intersect(dt2.Columns.OfType<DataColumn>(), new DataColumnComparer()); DataTable result = new DataTable(); dt1.PrimaryKey = commonColumns.ToArray(); result.Merge(dt1, false, MissingSchemaAction.AddWithKey); result.Merge(dt2, false, MissingSchemaAction.AddWithKey); |
基于Matthew的答案,我创建了一个接受2个以上数据表的函数。我希望它有助于:
用途:
1 | var table123 = FullOuterJoinDataTables(table1, table2, table3); |
以下是函数源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public DataTable FullOuterJoinDataTables(params DataTable[] datatables) // supports as many datatables as you need. { DataTable result = datatables.First().Clone(); var commonColumns = result.Columns.OfType<DataColumn>(); foreach (var dt in datatables.Skip(1)) { commonColumns = commonColumns.Intersect(dt.Columns.OfType<DataColumn>(), new DataColumnComparer()); } result.PrimaryKey = commonColumns.ToArray(); foreach (var dt in datatables) { result.Merge(dt, false, MissingSchemaAction.AddWithKey); } return result; } /* also create this class */ public class DataColumnComparer : IEqualityComparer<DataColumn> { public bool Equals(DataColumn x, DataColumn y) => x.Caption == y.Caption; public int GetHashCode(DataColumn obj) => obj.Caption.GetHashCode(); } |
我也很难得到答案,我正在复制粘贴整个代码。我相信这对你有帮助。
您只需要执行此联接的两个表中的
1 |
/ /您的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | /// <summary> /// Combines the data of two data table into a single data table. The grouping of tables /// will be based on the primary key provided for both the tables. /// </summary> /// <param name="table1"></param> /// <param name="table2"></param> /// <param name="table1PrimaryKey"></param> /// <param name="table2PrimaryKey"></param> /// <returns></returns> private DataTable DataTablesOuterJoin(DataTable table1, DataTable table2, string table1PrimaryKey, string table2PrimaryKey) { DataTable flatDataTable = new DataTable(); foreach (DataColumn column in table2.Columns) { flatDataTable.Columns.Add(new DataColumn(column.ToString())); } foreach (DataColumn column in table1.Columns) { flatDataTable.Columns.Add(new DataColumn(column.ToString())); } // Retrun empty table with required columns to generate empty extract if (table1.Rows.Count <= 0 && table2.Rows.Count <= 0) { flatDataTable.Columns.Remove(table2PrimaryKey); return flatDataTable; } var dataBaseTable2 = table2.AsEnumerable(); var groupDataT2toT1 = dataBaseTable2.GroupJoin(table1.AsEnumerable(), br => new { id = br.Field<string>(table2PrimaryKey).Trim().ToLower() }, jr => new { id = jr.Field<string>(table1PrimaryKey).Trim().ToLower() }, (baseRow, joinRow) => joinRow.DefaultIfEmpty() .Select(row => new { flatRow = baseRow.ItemArray.Concat((row == null) ? new object[table1.Columns.Count] : row.ItemArray).ToArray() })).SelectMany(s => s); var dataBaseTable1 = table1.AsEnumerable(); var groupDataT1toT2 = dataBaseTable1.GroupJoin(table2.Select(), br => new { id = br.Field<string>(table1PrimaryKey).Trim().ToLower() }, jr => new { id = jr.Field<string>(table2PrimaryKey).Trim().ToLower() }, (baseRow, joinRow) => joinRow.DefaultIfEmpty() .Select(row => new { flatRow = (row == null) ? new object[table2.Columns.Count].ToArray().Concat(baseRow.ItemArray).ToArray() : row.ItemArray.Concat(baseRow.ItemArray).ToArray() })).SelectMany(s => s); // Get the union of both group data to single set groupDataT2toT1 = groupDataT2toT1.Union(groupDataT1toT2); // Load the grouped data to newly created table foreach (var result in groupDataT2toT1) { flatDataTable.LoadDataRow(result.flatRow, false); } // Get the distinct rows only IEnumerable rows = flatDataTable.Select().Distinct(DataRowComparer.Default); // Create a new distinct table with same structure as flatDataTable DataTable distinctFlatDataTable = flatDataTable.Clone(); distinctFlatDataTable.Rows.Clear(); // Push all the rows into distinct table. // Note: There will be two different columns for primary key1 and primary key2. In grouped rows, // primary key1 or primary key2 can have empty values. So copy all the primary key2 values to // primary key1 only if primary key1 value is empty and then delete the primary key2. So at last // we will get only one perimary key. Please make sure the non-deleted key must be present in foreach (DataRow row in rows) { if (string.IsNullOrEmpty(row[table1PrimaryKey].ToString())) row[table1PrimaryKey] = row[table2PrimaryKey]; if (string.IsNullOrEmpty(row[CaptureBusDateColumn].ToString())) row[CaptureBusDateColumn] = _businessDate; if (string.IsNullOrEmpty(row[CaptureUserIDColumn].ToString())) row[CaptureUserIDColumn] = row[StatsUserIDColumn]; distinctFlatDataTable.ImportRow(row); } // Sort the table based on primary key. DataTable sortedFinaltable = (from orderRow in distinctFlatDataTable.AsEnumerable() orderby orderRow.Field<string>(table1PrimaryKey) select orderRow).CopyToDataTable(); // Remove primary key2 as we have already copied it to primary key1 sortedFinaltable.Columns.Remove(table2PrimaryKey); return ReplaceNulls(sortedFinaltable,"0"); } /// <summary> /// Replace all the null values from data table with specified string /// </summary> /// <param name="dt"></param> /// <param name="replaceStr"></param> /// <returns></returns> private DataTable ReplaceNulls(DataTable dt, string replaceStr) { for (int a = 0; a < dt.Rows.Count; a++) { for (int i = 0; i < dt.Columns.Count; i++) { if (dt.Rows[a][i] == DBNull.Value) { dt.Rows[a][i] = replaceStr; } } } return dt; } |