[英]LINQ join to return dynamic column list
我正在尋找一種從兩個數據表的 LINQ 連接返回動態列列表的方法。
首先,這不是重復的。 我已經研究和丟棄了:
如何執行行為與物理數據庫內部連接完全相同的 LINQ 連接?
(和許多其他人)
這是我的出發點:
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
的大型 CSV 文件(500K+ 條記錄)。DataTable
中的那些列。 如果傳入的兩個數據表碰巧有一個名稱相同的列,並且如果該列在我的列名數組中,只需傳回任一列,因為在這種情況下兩列之間的數據將相同。JoinDataTables()
在我的系統中從許多不同的地方被調用,以便加入各種各樣的 CSVs-turned-datatables,並且每個 CSV 文件都有非常不同的列。DataTable
表中返回所有列——只是我在columns
數組中指定的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
找到了這種方法的例子。
有任何想法嗎?
編輯:我每次都可以恢復所有列,但是我為包含所有列所做的每一次嘗試都導致了某種完全外部連接或交叉連接,返回的記錄數量比它應該的多。 所以,只要我沒有得到交叉連接,我就願意恢復所有列。
我不確定 500k 記錄的性能,但這是一個嘗試的解決方案。
由於您正在組合來自不同表的DataRow
的兩個子集,因此沒有簡單的操作可以創建子集或從子集創建新的DataTable
(盡管我有一個擴展方法來展平IEnumerable<anon>
where 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();
更新:通過替換rc1
和rc2
的定義,使用列序數而不是名稱來索引每個DataRow
可能是值得的:
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();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.