将数据表与特定条件组合

时间:2014-02-12 02:36:10

标签: c# linq loops datatable

更新:忘记提及,该表可能包含多种类型的商品代码

我的数据表有问题。共有14件商品具有相同的商品代码。现在有2张表来自不同的来源。一个按项目代码分组并总结数量,当计数等于10时,转到下一行,每行包含特定信息,如装运和备注。另一个表包含更多详细信息。

Source1:分组表

ItemCode|TotalQty|Shipment|Remarks|Line
=========================================
ITEM01  |  1000  |  S001  |  R001 |   1    <==10 items here
ITEM01  |  400   |  S002  |  R002 |   2    <==4 items here

Source2:详情表(14项&amp;行)

RefNo|ItemCode|Quantity|Weight|From
=======================================
R001 | ITEM01 |  100   | 50   | US
R002 | ITEM01 |  100   | 50   | US
R003 | ITEM01 |  100   | 50   | US
  .  |   .    |   .    |  .   |  .
  .  |   .    |   .    |  .   |  .
R013 | ITEM01 |  100   | 50   | US
R014 | ITEM01 |  100   | 50   | US

我想将source1和source2结合起来,得到如下结果

Shipment|Line|Remarks|ItemCode|TotalQty|RefNo|Quantity|Weight|From
===================================================================
  S001  | 1  | R001  | ITEM01 |  1000  | R001|  100   |  50  | US   \\1
  S001  | 1  | R001  | ITEM01 |  1000  | R002|  100   |  50  | US   \\2
  S001  | 1  | R001  | ITEM01 |  1000  | R003|  100   |  50  | US   \\3
  S001  | 1  | R001  | ITEM01 |  1000  | R004|  100   |  50  | US   \\4
  S001  | 1  | R001  | ITEM01 |  1000  | R005|  100   |  50  | US   \\5
  S001  | 1  | R001  | ITEM01 |  1000  | R006|  100   |  50  | US   \\6
  S001  | 1  | R001  | ITEM01 |  1000  | R007|  100   |  50  | US   \\7
  S001  | 1  | R001  | ITEM01 |  1000  | R008|  100   |  50  | US   \\8
  S001  | 1  | R001  | ITEM01 |  1000  | R009|  100   |  50  | US   \\9
  S001  | 1  | R001  | ITEM01 |  1000  | R010|  100   |  50  | US   \\10
  S002  | 2  | R002  | ITEM01 |  400   | R011|  100   |  50  | US   \\11
  S002  | 2  | R002  | ITEM01 |  400   | R012|  100   |  50  | US   \\12
  S002  | 2  | R002  | ITEM01 |  400   | R013|  100   |  50  | US   \\13
  S002  | 2  | R002  | ITEM01 |  400   | R014|  100   |  50  | US   \\14

有没有办法(Linq或循环)获得上述结果?谢谢你的帮助!

1 个答案:

答案 0 :(得分:0)

可以使用LINQ(我假设您已经完成,因为您已经包含LINQ标记),但不是我认为的漂亮的时尚。

鉴于两个DataTable个对象具有上述格式和数据,名为 grouped detail ,这是一个LINQ表达式,将以您希望的方式将数据拼接在一起:

IEnumerable<object[]> qry = 
    (
        from DataRow rDetail in detail.Rows 
        let dgrp = detail.Rows.IndexOf(rDetail) / 10

        join DataRow rGroup in grouped.Rows 
            on dgrp equals grouped.Rows.IndexOf(rGroup)

        orderby rDetail["RefNo"]

        select new object[] {
                rGroup["Shipment"], rGroup["Line"], rGroup["Remarks"], rGroup["ItemCode"], rGroup["TotalQty"],
                rDetail["RefNo"], rDetail["Quantity"], rDetail["Weight"], rDetail["From"]
            }
    );

现在您需要另一个DataTable将这些结果输入:

DataTable res = new DataTable();
res.Columns.Add("Shipment", typeof(string));
res.Columns.Add("Line", typeof(Int32));
res.Columns.Add("Remarks", typeof(string));
res.Columns.Add("ItemCode", typeof(string));
res.Columns.Add("TotalQty", typeof(Int32));
res.Columns.Add("RefNo", typeof(string));
res.Columns.Add("Quantity", typeof(Int32));
res.Columns.Add("Weight", typeof(Int32));
res.Columns.Add("From", typeof(string));

最后,在res表中填写LINQ查询的结果:

foreach (object[] rowdata in qry)
    res.Rows.Add(rowdata);

上面的代码适用于这个特定的数据集,但我不能向你保证更多。它在很大程度上依赖于源表中的行顺序,并且因为我使用DataTable.Rows.IndexOf来获取顺序,所以很可能这对于大量数据集来说非常慢。

但是你已经在使用DataTable而不是正确输入的集合了,所以无论如何,所有的赌注都在性能和代码完整性的赌注中。

这是我建议使用LINQ执行任务的一种情况。恕我直言,这将作为迭代循环而不是查询更好地完成。你没有比迭代版本获得更多(如果有的话)改进,你会失去很多清晰度,并且你可以在使用它之前设置好各种各样的乐趣。


因为我不能单独留下,所以这里是一个完整的(很长的代码)解决方案,使用LINQ的组合,用于保存正在处理的数据的类和用于生成表的迭代:

public DataTable MergeShippingData(DataTable groupTable, DataTable detailTable)
{
    // convert group table to array of GroupEntry objects
    var groupList = 
        (
            from DataRow grouprow in groupTable.Rows
            let ent = GroupEntry.FromRow(grouprow)
            where ent != null
            select ent
        ).ToArray();

    // convert detail table to sequence of DetailEntry objects
    var detailSeq = 
            from DataRow detailrow in detailTable.Rows
            let ent = DetailEntry.FromRow(detailrow)
            where ent != null
            select ent;

    // Create output DataTable
    DataTable output = CreateOutputTable();

    // Process all detail lines into shippings
    foreach (var detail in detailSeq)
    {
        // Find available shipping group for the item code with enough remaining capacity
        var grp = groupList.First (g => g.ItemCode == detail.ItemCode && g.Remainder >= detail.Quantity);
        if (grp == null)
            throw new Exception("No available shipping found for detail item...");

        // update remaining space in shipping group
        grp.Remainder -= detail.Quantity;

        // add data to output table
        output.Rows.Add(new object[] {
                grp.Shipment, grp.Line, grp.Remarks, grp.ItemCode, grp.TotalQty,
                detail.RefNo, detail.Quantity, detail.Weight, detail.From               
            });
    }

    return output;
}

// Class to hold the shipping groups while processing
public class GroupEntry
{
    // fields from source DataTable
    public string ItemCode;
    public int TotalQty;
    public string Shipment;
    public string Remarks;
    public int Line;

    // process variable, holds remaining quantity value
    public int Remainder;

    // Convert DataRow into GroupEntry
    public static GroupEntry FromRow(DataRow r)
    {
        try 
        {
            return new GroupEntry
            {
                ItemCode = r.Field<string>(0),
                TotalQty = r.Field<int>(1),
                Shipment = r.Field<string>(2),
                Remarks = r.Field<string>(3),
                Line = r.Field<int>(4),
                Remainder = r.Field<int>(1)
            };
        }
        catch { }
        return null;
    }
}

// Class to hold shipping Detail records during processing
public class DetailEntry
{
    public string RefNo;
    public string ItemCode;
    public int Quantity;
    public int Weight;
    public string From;

    // Convert DataRow into DetailEntry
    public static DetailEntry FromRow(DataRow r)
    {
        try
        {
            return new DetailEntry
            {
                RefNo = r.Field<string>(0),
                ItemCode = r.Field<string>(1),
                Quantity = r.Field<int>(2),
                Weight = r.Field<int>(3),
                From = r.Field<string>(4)
            };
        }
        catch { }
        return null;
    }
}

// Create output DataTable
public DataTable CreateOutputTable()
{
    DataTable res = new DataTable();
    res.Columns.Add("Shipment", typeof(string));
    res.Columns.Add("Line", typeof(Int32));
    res.Columns.Add("Remarks", typeof(string));
    res.Columns.Add("ItemCode", typeof(string));
    res.Columns.Add("TotalQty", typeof(Int32));
    res.Columns.Add("RefNo", typeof(string));
    res.Columns.Add("Quantity", typeof(Int32));
    res.Columns.Add("Weight", typeof(Int32));
    res.Columns.Add("From", typeof(string));

    return res;
}

添加一些错误处理,你就可以了。