我正在寻找一种从两个数据表的LINQ连接返回动态列列表的方法。
首先,这不是重复项。我已经研究并丢弃了:
C# LINQ list select columns dynamically from a joined dataset
Creating a LINQ select from multiple tables
How to do a LINQ join that behaves exactly like a physical database inner join?
(还有许多其他)
这是我的出发点:
public static DataTable JoinDataTables(DataTable dt1, DataTable dt2, string table1KeyField, string table2KeyField, string[] columns) {
DataTable result = ( from dataRows1 in dt1.AsEnumerable()
join dataRows2 in dt2.AsEnumerable()
on dataRows1.Field<string>(table1KeyField) equals dataRows2.Field<string>(table2KeyField)
[...I NEED HELP HERE with the SELECT....]).CopyToDataTable();
return result;
}
一些注意事项和要求:
DataTable
s中。DataTable
中返回那些列。如果传入的两个数据表都碰巧有一个相同的列,并且该列在我的列名称数组中,则只需传回任一列,因为在这种情况下,两列之间的数据相同。JoinDataTables()
是从系统中的许多不同位置调用的,以便连接各种CSV转换数据表,并且每个CSV文件都有非常不同的列。DataTable
中返回 all 列,而只是在columns
数组中指定的列。因此,假设在调用JoinDataTables()
之前,我具有以下2个数据表:
Table: T1
T1A T1B T1C T1D
==================
10 AA H1 Foo1
11 AB H1 Foo2
12 AA H2 Foo1
13 AB H2 Foo2
Table: T2
T2A T2X T2Y T2Z
==================
12 N1 O1 Yeah1
17 N2 O2 Yeah2
18 N3 O1 Yeah1
19 N4 O2 Yeah2
现在假设我们像这样连接这两个表:
ON T1.T1A = T2.T2A
select * from [join]
并产生以下结果集:
T1A T1B T1C T1D T2A T2X T2Y T2Z
====================================
12 AA H2 Foo1 12 N1 O1 Yeah1
请注意,联接仅产生1行。
现在到我的问题的症结所在。假设对于给定的用例,我只想从此联接中返回4列:T1A,T1D,T2A和T2Y。因此,我的结果集将如下所示:
T1A T1D T2A T2Y
==================
12 Foo1 12 O1
我希望能够这样调用我的JoinDataTables
函数:
DataTable dt = JoinDataTables(dt1, dt2, "T1A", "T2A", new string[] {"T1A", "T1D", "T2A", "T2Y"});
考虑到性能以及我不想遍历记录这一事实(因为处理大量数据的速度很慢),该如何实现? (该连接已经运行良好,现在我只需要一个正确的select
段(无论是通过new{..}
还是您想的方式)。
我无法接受函数内部具有硬编码列列表的解决方案。我在SO
上都找到了这种方法的示例。
有什么想法吗?
编辑:我可以每次都取回所有列,但我尝试包括所有列的每一次尝试都导致某种形式的FULL OUTER JOIN或CROSS JOIN,返回了数量级更高的记录比应该的因此,只要不交叉连接,我就会开放所有列。
答案 0 :(得分:0)
我不确定50万条记录的性能,但这是一个尝试的解决方案。
由于您要合并来自不同表的DataRow
的两个子集,因此没有简单的操作可以创建子集或从子集创建新的DataTable
(尽管我有一个扩展方法将IEnumerable<anon>
中的anon = new { DataRow1, DataRow2, ... }
从联接中展平,这对您来说可能很慢。
相反,我使用请求的列预先创建了一个答案DataTable
,然后使用LINQ来构建要添加为行的值数组。
public static DataTable JoinDataTables(DataTable dt1, DataTable dt2, string table1KeyField, string table2KeyField, string[] columns) {
var rtnCols1 = dt1.Columns.Cast<DataColumn>().Where(dc => columns.Contains(dc.ColumnName)).ToList();
var rc1 = rtnCols1.Select(dc => dc.ColumnName).ToList();
var rtnCols2 = dt2.Columns.Cast<DataColumn>().Where(dc => columns.Contains(dc.ColumnName) && !rc1.Contains(dc.ColumnName)).ToList();
var rc2 = rtnCols2.Select(dc => dc.ColumnName).ToList();
var work = from dataRows1 in dt1.AsEnumerable()
join dataRows2 in dt2.AsEnumerable()
on dataRows1.Field<string>(table1KeyField) equals dataRows2.Field<string>(table2KeyField)
select (from c1 in rc1 select dataRows1[c1]).Concat(from c2 in rc2 select dataRows2[c2]).ToArray();
var result = new DataTable();
foreach (var rc in rtnCols1)
result.Columns.Add(rc.ColumnName, rc.DataType);
foreach (var rc in rtnCols2)
result.Columns.Add(rc.ColumnName, rc.DataType);
foreach (var rowVals in work)
result.Rows.Add(rowVals);
return result;
}
由于您使用的是查询语法,所以我也这样做了,但是通常我可能会像这样:select
:
select rc1.Select(c1 => dataRows1[c1]).Concat(rc2.Select(c2 => dataRows2[c2])).ToArray();
已更新:通过替换DataRow
和rc1
的定义,可能值得使用列序号而不是名称来索引每个rc2
:
var rc1 = rtnCols1.Select(dc => dc.Ordinal).ToList();
var rc1Names = rtnCols1.Select(dc => dc.ColumnName).ToHashSet();
var rtnCols2 = dt2.Columns.Cast<DataColumn>().Where(dc => columns.Contains(dc.ColumnName) && !rc1Names.Contains(dc.ColumnName)).ToList();
var rc2 = rtnCols2.Select(dc => dc.Ordinal).ToList();