开发者

How to Show Records from a Required Parameters in C#?

SQL Datatype: SerialNumber/PartNumber - nvarchar(50), Quantity - int

Loaded Records on DataTable:

SerialNumber | PartNumber  | Quantity
      1      |     0001    |   20
      2      |     0001    |   10 
      3      |     0001    |   20  
      5      |     0001    |   20

Requirem开发者_JS百科ent:

1.Get Serials which SUM of their Quantity = 40

2.Get the lowest Serial first (FIFO)

Which Sample result should be like this:

SerialNumber | PartNumber  | Quantity
      1      |     0001    |   20
      3      |     0001    |   20 

How to get this result?

Thanks in Regards


Ok, so you removed the sql tags and introduced FIFO as a requirement. this clarifies matters.

the heart of your answer is here:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace SO_Forms_Demo
{
    class SumFifo
    {

        DataTable _dt;

        public SumFifo(DataTable dt)
        {
            _dt = dt;
        }

        public DataView GetAll()
        {
            return new DataView(_dt, null, null, DataViewRowState.CurrentRows);
        }

        public DataTable GetFIFO(string partNumber, int qty)
        {
            DataTable resultsTable = _dt.Clone();

            //the generic collection type that represents a FIFO relationship is a Queue
            Queue<DataRow> PartRows = new Queue<DataRow>(_dt.Select("partNumber = '" + partNumber + "'", "serialNumber"));

            //iterate through the queue adding rows and decreasing quantity till your requirment is met.
            foreach (DataRow row in PartRows)
            {
                if (qty > 0)
                {
                    resultsTable.ImportRow(row);
                    qty -= int.Parse(row["qty"].ToString());
                }
            }

            return resultsTable;
        }
    }
}

The "GetFIFO" method will iterate through the provided dataset and first create a queue (a FIFO collection of datarows that have the correct partNumber. It will iterate this collection decreasing the object quantity by the selected quantity until 0 is reached.

To test this build a form that looks like:

How to Show Records from a Required Parameters in C#?

With a code that looks like:

    using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace SO_Forms_Demo
{
    public partial class FormFIFO : Form
    {
        DataTable _table;
        public FormFIFO()
        {
            InitializeComponent();

            _table = new DataTable("fifo");
            _table.Columns.Add(new DataColumn("serialNumber"));
            _table.Columns.Add(new DataColumn("partNumber"));
            _table.Columns.Add(new DataColumn("qty"));
            _table.AcceptChanges();
            _table.Rows.Add(new object[3] { 1, "0001", 20 });
            _table.Rows.Add(new object[3] { 2, "0002", 10 });
            _table.Rows.Add(new object[3] { 3, "0001", 20 });
            _table.Rows.Add(new object[3] { 4, "0002", 10 });
            _table.Rows.Add(new object[3] { 5, "0001", 20 });
            _table.Rows.Add(new object[3] { 6, "0002", 10 });
            _table.AcceptChanges();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            dataGridView1.DataSource = null;
            SumFifo sumFifo = new SumFifo(_table);
            dataGridView1.DataSource = sumFifo.GetAll();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            dataGridView1.DataSource = null;
            SumFifo sumFifo = new SumFifo(_table);
            dataGridView1.DataSource = sumFifo.GetFIFO(textBox1.Text,int.Parse( textBox2.Text));
        }
    }
}

Now obviously you have a long way to go to make this "useful/robust/etc.." but the GetFIFO method is the essential answer to your question In C#.


This can definitely be done in SQL, but optimizing it to work for large sets will be the challenge. Here's a solution that makes use of Common Table Expressions to calculate the set of possible permutations for the target PartNumber, groups those permutations by the SUM of their Quantities and selects the first permutation whose SUM matches the target.

That permutation is then used to identify the SerialNumbers to select from the data set:

declare @partNum char(4)
SET @partNum = '0001'
declare @quantity int
SET @quantity = 40

declare @data TABLE (
    SerialNumber int identity(1,1),
    PartNumber char(4),
    Quantity int
);

INSERT INTO @data (PartNumber, Quantity) VALUES ('0001', 20);
INSERT INTO @data (PartNumber, Quantity) VALUES ('0001', 10);
INSERT INTO @data (PartNumber, Quantity) VALUES ('0001', 20);
INSERT INTO @data (PartNumber, Quantity) VALUES ('0002', 20);
INSERT INTO @data (PartNumber, Quantity) VALUES ('0001', 20);

WITH
cte_items as (
    select * from @data where PartNumber = @partNum
),
cte_perms as (
    select cast(cast(SerialNumber as binary(4)) as varbinary(max)) as perm, 1 as numentries
    from cte_items
    union all
    select cast(n.SerialNumber as binary(4)) + p.perm, p.numentries + 1
    from cte_perms p
    join cte_items n on n.SerialNumber < cast(substring(perm,1,4) as int)
),
cte_permlist as (
    select row_number() over (order by (select 1)) as permnum, perm
    from cte_perms
)

SELECT d1.SerialNumber, d1.PartNumber, d1.Quantity
FROM @data d1
INNER JOIN (
    SELECT
       cast(substring(p.perm, 4*n.SerialNumber-3, 4) as int) as SerialNumber
    from cte_permlist p
    join @data n on n.SerialNumber = n.SerialNumber
    where cast(substring(p.perm, 4*n.SerialNumber-3, 4) as int) != 0
      and p.permnum = (
            SELECT TOP 1 permutations.permnum
            FROM @data d2
            CROSS APPLY (
                SELECT
                   p.permnum,
                   cast(substring(p.perm, 4*n.SerialNumber-3, 4) as int) as SerialNumber
                from cte_permlist p
                join @data n on n.SerialNumber = n.SerialNumber
                where cast(substring(p.perm, 4*n.SerialNumber-3, 4) as int) != 0
            ) permutations
            WHERE PartNumber = @partNum
              and permutations.SerialNumber = d2.SerialNumber
            GROUP BY permutations.permnum
            HAVING SUM(d2.Quantity) = @quantity
            ORDER BY permnum desc
        )
    ) pSn on pSn.SerialNumber = d1.SerialNumber

The result:

SerialNumber PartNumber Quantity
------------ ---------- -----------
1            0001       20
3            0001       20

Once the query optimizer gets done with this it should be quite efficient unless, for the target part number, there are more than a handful of permutations.


I'd suggest writing a SQL query that returns all rows for a given part number and perform the calculations to determine which Serial #'s to use in a different programming language. SQL is great for set-based operations, but this problem requires finding a particular constrained subset of a subset which SQL won't excel at.

When you've gotten your row collection for the part. I'd suggest employing a depth- or breadth- first search. Depth-first would probably be the best approach if you've no requirement to fulfil the quantity in 'the minimum number of serial #s' - and choosing 'the largest quantity less than the shortfall' at each stage should be a reasonable strategy when progressing the search.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜