2014-02-12 6 views
0

ОБНОВЛЕНИЕ: Забыл упомянуть, таблица может содержать более одного типа АртикулОбъединить DataTables с конкретными условиями

У меня проблема для DataTable. Всего 14 предметов с одинаковым кодом элемента. И теперь есть 2 таблицы из разных источников. Один из них сгруппирован по коду item и суммирует количество, когда число равно 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 пунктов & строк)

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 или looping), чтобы получить вышеуказанный результат? Спасибо за вашу помощь!

+1

Между вашим входом и выходом нет очевидной связи. Ожидаете ли вы, что элементы из таблицы «Деталь» должны быть привязаны к записи из таблицы «Группировка» на основе общей суммы поля «Количество»? – Corey

+0

Да. В принципе, элементы в источнике 1 и 2 одинаковы. Но мне нужно напомнить, что «Отгрузка», «Заметки» и «Линия» будут назначаться каждые 10 позиций одним и тем же кодом. –

ответ

0

Это можно сделать с помощью LINQ (который, как я полагаю, вам нужен, поскольку вы включили тег LINQ), но не в том, что я бы рассмотрел как способ nice.

Учитывая два 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 s вместо правильно подобранной коллекции, поэтому все ставки в любом случае не влияют на производительность и код.

Это один случай, когда я предлагаю не с помощью 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; 
} 

Включите обработку ошибок, и вы хорошо пойдете.

+0

Спасибо за помощь! Но это только для одного типа предметов? Поскольку я пытался использовать 2 типа itemcode (ITEM01 & ITEM02), он не может работать –

+0

. В исходный набор данных не включен второй тип элемента, поэтому я не знал, что это условие. На этом этапе я определенно не создавал бы оператор LINQ - вместо этого перебирал бы записи, внешний цикл для списка «grouped», внутренний цикл для списка «detail». – Corey

+0

ОК, так что этот меня прослушивал достаточно, чтобы написать полное решение. Одиночный цикл над подробными записями, обрабатывает несколько кодов элементов, выделяет детали для групп на основе количества и т. Д. Бросил кучу комментариев для объяснения. – Corey

Смежные вопросы