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,因此显然不能使用
您真正需要的(是否是LINQ)是通过
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); } } |
由于您在过程中向
1 | dr1ByXNo.Add(dr0.Field<string>(X_NO), dr0); |
我没有包含它,因为我看不到您的代码设置了新的记录x_no字段,所以上面的代码将产生重复的键异常。