簡體   English   中英

C# - Linq 將兩個數據表合並(或)合並為一個

[英]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.

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