簡體   English   中英

LINQ join 返回動態列列表

[英]LINQ join to return dynamic column list

我正在尋找一種從兩個數據表的 LINQ 連接返回動態列列表的方法。

首先,這不是重復的。 我已經研究和丟棄了:

C# LINQ 列表從連接的數據集中動態選擇列

從多個表中創建 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;
}

一些注意事項和要求:

  1. 沒有數據庫引擎。 數據源是讀取到 c# DataTable的大型 CSV 文件(500K+ 條記錄)。
  2. 由於 CSV 很大,出於性能原因,循環訪問連接中的每條記錄是一個糟糕的解決方案。 我已經嘗試過記錄循環,但它太慢了。 我在上面的連接上獲得了很好的性能,但我找不到一種方法讓它只返回我想要的列(由調用者指定)而不循環記錄。
  3. 如果我需要循環連接中的,那很好,我只是不想循環行。
  4. 我希望能夠傳入一個列名數組並只返回結果DataTable中的那些列。 如果傳入的兩個數據表碰巧有一個名稱相同的列,並且如果該列在我的列名數組中,只需傳回任一列,因為在這種情況下兩列之間的數據將相同。
  5. 如果我需要傳入 2 個數組(每個數據表所需的列 1 個),那很好,但 1 個列名數組將是理想的。
  6. 列列表不能是靜態的並且不能硬編碼到函數中。 原因是因為我的JoinDataTables()在我的系統中從許多不同的地方被調用,以便加入各種各樣的 CSVs-turned-datatables,並且每個 CSV 文件都有非常不同的列。
  7. 我不希望在結果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();

更新:通過替換rc1rc2的定義,使用列序數而不是名稱來索引每個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM