[英]C# - Linq to combine (or) join two datatables into one
我在通過在 C# 中使用 Linq 將兩個數據表中的正確數據合並為一個時遇到問題。
我的數據表的數據來自 Excel 文件讀取(不是來自 DB)。
我在 linq 下嘗試過,但返回行數不是我想要的(我的目標是檢索所有數據,但為了驗證,我正在檢查行數,以便我知道它是否正確)。
在 dt1 中,我有 2645 條記錄。
在 dt2 中,我有 2600 條記錄。
返回行數為 2600(看起來它正在執行正確的連接邏輯)。
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);
我嘗試使用“加入”,但我的問題是 X_NO 值可以在 X_ITEM_CODE 或 X_ITEM_KEY 中,所以我只能在 xxx 等於 yyy 中放置一個條件。
如果我的上述條件也適合使用,我想嘗試“加入”。 請給我一些指導。 謝謝。
[附加信息]
我已經嘗試過 foreach 循環 + dt1.Select(xxxx) + dt1.Rows.Add(xxx),它運行良好,但大約需要 2 分鍾才能完成這項工作。
我正在尋找一種更快的方法,從上面我嘗試過的 Linq 代碼,它似乎比我的 foreach 循環更快,所以我想給 Linq 一個機會。
出於演示目的,我在上面的例子中只放了幾列,我的實際列數是 12 列。
如果我使用 foreach 循環,我擔心我的帖子會變得很長,所以當我發布這個問題時我會跳過它。
無論如何,下面是代碼和示例數據。 對於那些可以編輯並認為它太長的人,請刪除不必要/不相關的代碼或行。
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,,,,,....
[dt2 數據]
X_ITEM_CODE,X_ITEM_KEY,COST,DESC,QTY
AA060210A,AA060211A,100.00,PART1,10000
AB060221A,AB060220A,120.00,PART2,500
AC060232A,AC060230A,150.00,PART3,100
AD060240A,AD060243A,4.50,PART4,15250
[更新2]
我在“加入”下面嘗試過,但它什么也沒返回。 那么,我可以假設加入也無濟於事嗎?
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 僅支持equijoins ,因此顯然不能使用join
運算符。 但是將 LINQ 查詢與笛卡爾積和where
一起使用不會給您帶來任何性能改進。
您真正需要的(是否為 LINQ)是通過dt1[X_NO]
字段進行快速查找。 正如您所說,它是獨一無二的,因此您可以為此構建和使用字典:
var dr1ByXNo = dt1.AsEnumerable().ToDictionary(dr => dr.Field<string>(X_NO));
然后像這樣修改你的過程:
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);
行之后)添加以下內容
dr1ByXNo.Add(dr0.Field<string>(X_NO), dr0);
我沒有包含它,因為我沒有看到你的代碼設置新記錄 X_NO 字段,所以上面會產生重復鍵異常。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.