关于C#:C# – Linq将两个数据表合并(或)连接成一个

C# - Linq to combine (or) join two datatables into one

我在使用c_中的linq将两个数据表中的正确数据转换为一个数据表时遇到问题。
我的数据表数据来自Excel文件读取(不是来自数据库)。
BR/>我在linq下面尝试过,但返回行数不是我想要的(我的目标是检索所有数据,但为了验证,我检查行数,以便知道它是正确的还是不容易的)。
BR/>在DT1中,我有2645条记录。< BR>在DT2中,我有2600条记录。
BR/>返回行计数为2600(看起来它执行的是右联接逻辑)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var v1 = from d1 in dt1.AsEnumerable()
         from d2 in dt2.AsEnumerable()
         .Where(x => x.Field<string>(X_ITEM_CODE) == d1.Field<string>(X_NO)
         || x.Field<string>(X_ITEM_KEY) == d1.Field<string>(X_NO))
         select dt1.LoadDataRow(new object[]
         {
             // I use short cut way instead of Field<string> for testing purpose.
             d1[X_NO],
             d2[X_ITEM_CODE] == null ?"" : d2[X_ITEM_CODE] ,
             d2[X_ITEM_KEY] == null ?"" : d2[X_ITEM_KEY],
             d2[X_COSTS],
             d2[X_DESC],
             d2[X_QTY]== null ? 0 : dt[X_QTY]
         }, false);

         dt1 = v1.CopyToDataTable();
         Console.WriteLine(dt1.Rows.Count);

< BR>我尝试使用"join",但我的问题是x_no value可以是x_item_code或x_item_key,所以我只能在xxx等于yyy上输入一个条件。
BR/>如果我的上述条件也适合使用,我想尝试"加入"。请给我一些指导。谢谢。

[附加信息]
我已经尝试了foreach loop+dt1。select(xxxx)+dt1.rows.add(xxx),它工作得很好,但大约需要2分钟来完成作业。
我正在寻找一种更快的方法,从上面的linq代码来看,它似乎比我的foreach循环更快,所以我想给linq一个机会。< BR>为了演示的目的,我只在上面的示例中放置了几列,实际的列数是12列。
BR/>我担心我的职位会变得很长,如果我把我的前臂循环,所以我跳过它,当我张贴这个问题。< BR>总之,下面是代码和示例数据。对于那些可以编辑并且认为它太长的人,请去掉不必要的/不相关的代码或行。

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
DataRow[] drs = null;
DataRow drO = null;

foreach (DataRow drY in dt2.Rows)
{
    drs = null;
    drs = dt1.Select(X_NO +"='" + drY[X_ITEM_KEY] +"' OR" + X_NO +"='" + drY[X_ITEM_CODE] +"'");
    if (drs.Length >= 0)
    {
        // drs Leng will always 1 because no duplicate.
        drs[0][X_ITEM_CODE] = drY[X_ITEM_CODE];
        drs[0][X_ITEM_KEY] = drY[X_ITEM_KEY];
        drs[0][X_COST] = clsD.GetInt(drY[X_COST]);      // If null, return 0.
        drs[0][X_DESC] = clsD.GetStr(drY[X_DESC]);      // If null, return"".
        drs[0][X_QTY] = clsD.GetInt(drY[X_QTY]);
    }
    else
    {
        // Not Found in ITEM CODE or KEY, add it.
        drO = dtOutput.NewRow();
        drO[X_ITEM_CODE] = drY[X_ITEM_CODE];
        drO[X_ITEM_KEY] = drY[X_ITEM_KEY];
        drO[X_COST] = clsD.GetInt(drY[X_COST]);
        drO[X_DESC] = clsD.GetStr(drY[X_DESC]);
        drO[X_QTY] = clsD.GetInt(drY[X_QTY]);
        dt1.Rows.Add(drO);
    }
}
// Note: For above else condition, I didn't put in my Linq testing yet.
// If without else condition, my dt1 will still have same record count.

[DT1数据]
x_no,x_item_code,x_item_key,cost,desc,qty,…。
aa060210a,,,,,……
AB060220A,,,……
AC060230A,,,……
AD060240A,,,……

BR/>[DT2数据]
x_item_code,x_item_key,cost,desc,qty
aa060210a,aa060211a,100.00,part110000
AB060221A,AB060220A,120.00,零件2500
AC060232A,AC060230A,150.00,第3100部分
AD060240A、AD060243A、4.50、415250部分

[更新2]
我在"加入"下面尝试过,但没有回报。那么,我能假设join也不会有帮助吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var vTemp1 = from d1 in dt1.AsEnumerable()
             join d2 in dt2.AsEnumerable()
             on 1 equals 1
             where (d1[X_NO] == d2[X_ITEM_CODE] || d1[X_NO] == d2[X_ITEM_KEY])
             select dt1.LoadDataRow(new object[]
             {
                d1[X_NO],
                d2[X_ITEM_CODE] == null ?"" : d2[X_ITEM_CODE] ,
                d2[X_ITEM_KEY] == null ?"" : d2[X_ITEM_KEY],
                d2[X_COST],
                d2[X_DESC],
                d2[X_QTY]== null ? 0 : d2[X_QTY]
             }, false);

Console.WriteLine(vTemp1.Count()); // return zero.


LINQ只支持equijoin,因此显然不能使用join运算符。但是使用带有笛卡尔积和where的LINQ查询不会给您带来任何性能改进。

您真正需要的(是否是LINQ)是通过dt1[X_NO]字段快速查找。因为正如你所说,它是独一无二的,所以你可以建立和使用一本字典:

1
var dr1ByXNo = dt1.AsEnumerable().ToDictionary(dr => dr.Field<string>(X_NO));

然后像这样修改您的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
foreach (DataRow drY in dt2.Rows)
{
    if (dr1ByXNo.TryGetValue(drY.Field<string>(X_ITEM_KEY), out dr0) ||
        dr1ByXNo.TryGetValue(drY.Field<string>(X_ITEM_CODE), out dr0))
    {
        dr0[X_ITEM_CODE] = drY[X_ITEM_CODE];
        dr0[X_ITEM_KEY] = drY[X_ITEM_KEY];
        dr0[X_COST] = clsD.GetInt(drY[X_COST]);      // If null, return 0.
        dr0[X_DESC] = clsD.GetStr(drY[X_DESC]);      // If null, return"".
        dr0[X_QTY] = clsD.GetInt(drY[X_QTY]);
    }
    else
    {
        // Not Found in ITEM CODE or KEY, add it.
        drO = dtOutput.NewRow();
        drO[X_ITEM_CODE] = drY[X_ITEM_CODE];
        drO[X_ITEM_KEY] = drY[X_ITEM_KEY];
        drO[X_COST] = clsD.GetInt(drY[X_COST]);
        drO[X_DESC] = clsD.GetStr(drY[X_DESC]);
        drO[X_QTY] = clsD.GetInt(drY[X_QTY]);
        dt1.Rows.Add(drO);
    }
}

由于您在过程中向dt1添加新记录,根据您的要求,您可能需要在else的末尾(在dt1.Rows.Add(drO);行之后)添加以下内容

1
dr1ByXNo.Add(dr0.Field<string>(X_NO), dr0);

我没有包含它,因为我看不到您的代码设置了新的记录x_no字段,所以上面的代码将产生重复的键异常。