【问题标题】:Combine datatables with specific conditions结合特定条件的数据表
【发布时间】:2014-02-12 02:36:10
【问题描述】:

更新:忘了说,该表可能包含不止一种类型的 itemcode

我遇到了数据表问题。共有 14 个项目具有相同的项目代码。现在有 2 个表来自不同的来源。一个是按itemcode分组并求和数量,当count等于10时,转到下一行,每行包含出货、备注等具体信息。另一个表包含更多详细信息。

Source1:分组表

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

来源 2:明细表(14 项和行)

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 或循环)来获得上述结果?感谢您的帮助!

【问题讨论】:

  • 输入和输出之间没有明显的联系。您是否希望根据Quantity 字段的运行总计将Detail 表中的项目分配给Grouped 表中的记录?
  • 是的。基本上,源 1 和 2 中的项目是相同的。但我需要提醒的是,Shipment、Remarks 和 line 将被分配每 10 件具有相同 itemcode 的项目。

标签: c# linq loops datatable


【解决方案1】:

它可以用 LINQ 来完成(我假设你在使用它,因为你已经包含了 LINQ 标签),但不是我认为的 nice 时尚。

给定两个具有上述格式和数据的 DataTable 对象,分别命名为 groupeddetail,这是一个 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));

最后,用 LINQ 查询的结果填充res 表:

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

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

但是你已经在使用DataTables 而不是正确类型的集合,所以无论如何,所有赌注都在性能和代码健全性上。

这是我建议使用 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;
}

添加一些错误处理,一切顺利。

【讨论】:

  • 感谢您的帮助!但它只适用于单一类型的项目?因为我尝试使用 2 种类型的项目代码(ITEM01 和 ITEM02),它无法工作
  • 您的原始数据集不包含第二个项目类型,所以我不知道这是一个条件。在这一点上,我绝对不会构建 LINQ 语句 - 而是遍历记录,grouped 列表的外循环,detail 列表的内循环。
  • 好的,所以这个问题足以让我编写一个完整的解决方案。单循环遍历明细记录,处理多个item code,根据数量将明细分配给group等。扔了一堆cmets来解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-07
  • 2021-06-01
  • 1970-01-01
  • 2018-04-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多