根据需要的数量选择发票

时间:2015-09-29 06:50:07

标签: sql sql-server sql-server-2008

我有一个看起来像这样的表:

+---------------+---------------+------------------+--------------+
| InvoiceNumber | ProductNumber | ReceivedQuantity | ReceivedDate |
+---------------+---------------+------------------+--------------+
| INV001        | P001          |              500 |   09/01/2015 |
| INV002        | P001          |              600 |   09/02/2015 |
| INV003        | P001          |              700 |   09/03/2015 |
+---------------+---------------+------------------+--------------+

订购产品时。系统需要知道它从哪个发票中获取。先进先出。

例如,我需要1000个产品编号P001。它应该选择以下发票。它不显示最后一张发票,因为500 + 600已经足够数量

+---------------+---------------+------------------+--------------+
| InvoiceNumber | ProductNumber | ReceivedQuantity | ReceivedDate |
+---------------+---------------+------------------+--------------+
| INV001        | P001          |              500 |   09/01/2015 |
| INV002        | P001          |              600 |   09/02/2015 |
+---------------+---------------+------------------+--------------+

我可以通过制作游标并循环遍历表来复制这个,但是寻找实现这一目标的最佳方法。任何向正确方向的推动都会有很大帮助。

2 个答案:

答案 0 :(得分:2)

我认为你可以使用这样的查询:

;WITH t As (
    SELECT *
        , ROW_NUMBER() OVER (ORDER BY ReceivedDate, InvoiceNumber) As RowNo
    FROM yourTable
), firstOverflow AS (
    SELECT TOP(1)
        t1.RowNo
    FROM t t1
        LEFT JOIN
        t t2 ON t1.ProductNumber = t2.ProductNumber AND t1.ReceivedDate >= t2.ReceivedDate
    GROUP BY t1.RowNo, t1.InvoiceNumber, t1.ProductNumber, t1.ReceivedQuantity, t1.ReceivedDate
    HAVING SUM(t2.ReceivedQuantity) >= 1000
    ORDER BY SUM(t2.ReceivedQuantity) - 1000)
SELECT *
FROM t 
    JOIN 
    firstOverflow ON t.RowNo <= firstOverflow.RowNo;

更好的解决方案是:

DECLARE @value int = 1000;

WITH t As (
    SELECT *
        , ROW_NUMBER() OVER (ORDER BY ReceivedDate, InvoiceNumber) As seq
    FROM yourTable
), s As (
    SELECT t.InvoiceNumber, t.ProductNumber, t.ReceivedQuantity, t.ReceivedDate, SUM(tt.ReceivedQuantity) As currentTotal
    FROM t
        LEFT JOIN
        t tt ON t.ProductNumber = tt.ProductNumber AND  t.seq >= tt.seq
    GROUP BY t.InvoiceNumber, t.ProductNumber, t.ReceivedQuantity, t.ReceivedDate
), st As (
    SELECT *
        , ROW_NUMBER() OVER (ORDER BY (CASE WHEN s.currentTotal > @value THEN -currentTotal ELSE Null END) DESC) As seq
    FROM s)
SELECT st.InvoiceNumber, st.ProductNumber, st.ReceivedQuantity, st.ReceivedDate
FROM st
WHERE currentTotal < @value
UNION ALL
SELECT st.InvoiceNumber, st.ProductNumber, st.ReceivedQuantity, st.ReceivedDate
FROM st
WHERE currentTotal >= @value AND st.seq = 1;

答案 1 :(得分:0)

尝试此查询并提供一些反馈:

DECLARE @table TABLE (InvoiceNumber nvarchar(100),
                      ProductNumber nvarchar(100),
                      ReceivedQuantity int)
INSERT INTO @table VALUES ('inv001', 'p001', 500)
INSERT INTO @table VALUES ('inv002', 'p001', 600)
INSERT INTO @table VALUES ('inv003', 'p001', 600)
INSERT INTO @table VALUES ('inv004', 'p001', 600)

SQL 2012:

SELECT v.* FROM
(
    SELECT t.*,
      SUM(ReceivedQuantity) OVER (PARTITION BY ProductNumber ORDER BY InvoiceNumber) AS sum
    FROM @table t
) v
WHERE sum <= 1000

SQL 2008:

SELECT v.* FROM
(
SELECT
    a.InvoiceNumber
    , a.ProductNumber
    , SUM(b.ReceivedQuantity) AS sum
FROM
    @table a
    INNER JOIN @table b
    ON  a.InvoiceNumber >= b.InvoiceNumber AND a.ProductNumber = b.ProductNumber
GROUP BY 
    a.InvoiceNumber
    , a.ProductNumber
) v
WHERE sum <= 1000