繁体   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