sql-server - 当 SQL 表不是一对一时,如何计算准时交货

标签 sql-server sql-server-2008

我有以下两个表:

截止日期:

DECLARE @DueDates TABLE (
    uniqueln varchar(10),
    ship_dts smalldatetime,
    qty decimal(18,4))
INSERT INTO @DueDates
SELECT '51351621AS','1/1/2013',7
UNION ALL
SELECT '51351621AS','1/7/2013',7
UNION ALL
SELECT '51351621AS','1/14/2013',7
UNION ALL
SELECT '51351621AS','1/21/2013',7
UNION ALL
SELECT '51351621AS','1/28/2013',7
UNION ALL
SELECT 'V4351621AS','1/5/2013',10
UNION ALL
SELECT 'V4351621AS','1/10/2013',10
UNION ALL
SELECT 'V4351621AS','1/15/2013',10
UNION ALL
SELECT 'V4351621AS','1/20/2013',10
UNION ALL
SELECT 'V4351621AS','1/25/2013',10

详细信息

DECLARE @PlDetail TABLE (
    uniqueln varchar(10),
    shipdate smalldatetime,
    shippedqty decimal(18,4))
INSERT INTO @PlDetail
SELECT '51351621AS','1/1/2013',10
UNION ALL
SELECT '51351621AS','1/7/2013',10
UNION ALL
SELECT '51351621AS','1/14/2013',10
UNION ALL
SELECT '51351621AS','1/21/2013',5
UNION ALL
SELECT 'V4351621AS','1/5/2013',13
UNION ALL
SELECT 'V4351621AS','1/15/2013',9
UNION ALL
SELECT 'V4351621AS','1/25/2013',12
UNION ALL
SELECT 'V4351621AS','1/30/2013',10
UNION ALL
SELECT 'V4351621AS','2/5/2013',6

PlDetail 中的发货可以与 DueDates 表中的订单一一对应,但通常情况并非如此。

我正在尝试使用 FIFO 方法计算每个 uniqueln 计划的准时交货(我无法更改数据在表中的存储方式)。基本上,我想将最早发货应用于最早交付。

如果发货数量超过 DueDates 记录中的余额,则应将余额应用于下一次计划的发货。

最终结果应该如下所示:

uniqueln    ship_dts    qty shipdate    shippedqty  daysLate
51351621AS  1/1/2013    7   1/1/2013    7            0
51351621AS  1/7/2013    7   1/1/2013    3           -6
51351621AS  1/7/2013    7   1/7/2013    4            0
51351621AS  1/14/2013   7   1/7/2013    6           -7
51351621AS  1/14/2013   7   1/14/2013   1            0
51351621AS  1/21/2013   7   1/14/2013   7           -7
51351621AS  1/28/2013   7   1/14/2013   2           -14
51351621AS  1/28/2013   7   1/21/2013   5           -7
V4351621AS  1/5/2013    10  1/5/2013    10           0
V4351621AS  1/10/2013   10  1/5/2013    3           -5
V4351621AS  1/10/2013   10  1/15/2013   7            5
V4351621AS  1/15/2013   10  1/15/2013   2            0
V4351621AS  1/15/2013   10  1/25/2013   8           10
V4351621AS  1/20/2013   10  1/25/2013   4            5
V4351621AS  1/20/2013   10  1/30/2013   6           10
V4351621AS  1/25/2013   10  1/30/2013   4            5
V4351621AS  1/25/2013   10  2/5/2013    6           11

我知道如何严格按日期对 PlDetail 发货进行分组,以便将下一个到期日或之前的任何发货分组在一起,但它必须考虑计划数量与发货数量.

我不想创建游标并循环浏览发货记录,但如果这种类型的联接不起作用,我可以这样做。但是,我相信这是可能的,但我不确定如何对表进行分组或连接。

听起来 SQL Server 2012 有一些新方法可以使这一切变得更容易,但现在我正在使用 SQL SERVER 2008 R2,并且在不久的将来必须保持这种状态。

解决这个问题的最佳方法是什么?光标真的是唯一的方法吗?

更新: 这是我到目前为止所添加的内容。最终结果是一个表格,显示每个 uniqueln 的起止数量和ship_dts

WITH DSeq AS (
   SELECT TOP 100 PERCENT
      Seq = Row_Number() OVER (partition by uniqueln ORDER BY ship_dts),
      D.UNIQUELN,
      D.SHIP_DTS,
      SchBal = (SELECT TOP 100 PERCENT SUM(B.Qty) FROM @DueDates B WHERE b.SHIP_DTS<= D.SHIP_DTS AND b.UNIQUELN=d.UNIQUELN ORDER BY d.SHIP_DTS)
   FROM @DueDates D
   GROUP BY UNIQUELN,D.QTY,D.UNIQUELN,D.SHIP_DTS
   ORDER BY D.UNIQUELN, D.SHIP_DTS
)
--SELECT * FROM DSeq
, Slices AS (
   SELECT
      LN = D.UNIQUELN,
      FromQty = COALESCE(N.SchBal,0),
      ToQty = D.SchBal,
      D.SHIP_DTS
   FROM
      DSeq D
      LEFT OUTER JOIN DSeq N
         ON D.Seq -1 = N.Seq AND D.UNIQUELN=N.UNIQUELN
)
SELECT * FROM Slices

光标方法:

如前所述,最好的方法可能是游标。希望有人有一个可以满足所有需求的连接方法,但在那之前我们使用的是游标。

如果有人正在寻找使用游标方法的解决方案,下面的代码就是我们的实现方法。用户选择一个日期范围并生成结果。请注意,您必须对 uniqueln 的所有记录运行游标,即使它们在所选日期范围之前发货,否则发货的 FIFO 应用程序将是错误的。

DECLARE @startdate smalldatetime, @endDate smalldatetime

DECLARE @OnTime TABLE (Uniqueln varchar(10),DueQty int,dueDate smalldatetime,shipDate smalldatetime,shipQty int DEFAULT 0,daysLate int,balQty int)


DECLARE @uniqln1 varchar(10),@toQty int, @dueDate smalldatetime,@bQty int
DECLARE @uniqln2 varchar(10),@shipQty int, @shipDate smalldatetime

DECLARE ot_cursor CURSOR LOCAL FAST_FORWARD
FOR
SELECT uniqueln,qty,ship_dts,qty
    FROM @DueDates 
    ORDER BY uniqueln,ship_dts 
OPEN ot_cursor;
FETCH NEXT FROM ot_cursor INTO @uniqln1,@toQty,@dueDate,@bQty

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE s_cursor CURSOR LOCAL FAST_FORWARD
    FOR
    SELECT Uniqueln,shippedqty,shipdate
        FROM @PlDetail p
        WHERE uniqueln = @uniqln1
        ORDER BY 3
    OPEN s_cursor ;
    FETCH NEXT FROM s_cursor INTO @uniqln2,@shipQty,@shipDate
    WHILE @@FETCH_STATUS = 0
    BEGIN
        INSERT INTO @OnTime(Uniqueln,DueQty,dueDate,shipDate,shipQty,daysLate,balQty)
        SELECT @uniqln1,@toQty,@dueDate,@shipDate,CASE WHEN @bQty>@shipQty THEN @shipQty ELSE @bQty END,DATEDIFF(d,@dueDate,@shipDate),CASE WHEN @bQty>@shipQty THEN @bQty-@shipQty ELSE 0 END
        SET @bQty=@bQty-@shipQty
        IF @bQty < 0 
        BEGIN
            SET @shipQty = -@bQty 
            FETCH NEXT FROM ot_cursor INTO @uniqln1,@toQty,@dueDate,@bQty
        END
        ELSE 
        IF @bQty =0
        BEGIN
            BREAK
        END
        ELSE
        BEGIN
            SET @shipQty = @bQty
            FETCH NEXT FROM s_cursor INTO @uniqln2,@shipQty,@shipDate
        END
    END
    CLOSE s_cursor
    DEALLOCATE s_cursor
    FETCH NEXT FROM ot_cursor INTO @uniqln1,@toQty,@dueDate,@bQty
END
CLOSE ot_cursor
DEALLOCATE ot_cursor

SELECT * FROM @OnTime 
    WHERE shipDate BETWEEN @startdate AND @endDate
    ORDER BY Uniqueln,dueDate 

最佳答案

尝试使用 CTE 和表之间的洋泾浜交叉连接将某些内容放在一起。 (别问,这很丑)

任何人,经过一点reading ,并且知道表格有多大,我觉得可以很安全地用...击鼓...使用光标来回答这个问题。这将是丑陋且缓慢的,但逻辑在纸面上会更有意义。关于可维护的代码有很多话要说...

更新:去度假。这就是我正在玩的东西。

DECLARE @DueDates TABLE (
    uniqueln varchar(10),
    ship_dts smalldatetime,
    qty decimal(18,4))
INSERT INTO @DueDates
SELECT '51351621AS','1/1/2013',7
UNION ALL
SELECT '51351621AS','1/7/2013',7
UNION ALL
SELECT '51351621AS','1/14/2013',7
UNION ALL
SELECT '51351621AS','1/21/2013',7
UNION ALL
SELECT '51351621AS','1/28/2013',7
UNION ALL
SELECT 'V4351621AS','1/5/2013',10
UNION ALL
SELECT 'V4351621AS','1/10/2013',10
UNION ALL
SELECT 'V4351621AS','1/15/2013',10
UNION ALL
SELECT 'V4351621AS','1/20/2013',10
UNION ALL
SELECT 'V4351621AS','1/25/2013',10


DECLARE @PlDetail TABLE (
    uniqueln varchar(10),
    shipdate smalldatetime,
    shippedqty decimal(18,4))
INSERT INTO @PlDetail
SELECT '51351621AS','1/1/2013',10
UNION ALL
SELECT '51351621AS','1/7/2013',10
UNION ALL
SELECT '51351621AS','1/14/2013',10
UNION ALL
SELECT '51351621AS','1/21/2013',5
UNION ALL
SELECT 'V4351621AS','1/5/2013',13
UNION ALL
SELECT 'V4351621AS','1/15/2013',9
UNION ALL
SELECT 'V4351621AS','1/25/2013',12
UNION ALL
SELECT 'V4351621AS','1/30/2013',10
UNION ALL
SELECT 'V4351621AS','2/5/2013',6



; WITH DueDates AS 
(
    SELECT b.*
    FROM @DueDates a
    JOIN @DueDates b
        ON a.uniqueln = b.uniqueln
        AND b.ship_dts >= a.ship_dts
)
, PlDetail AS
(
    SELECT b.*
    FROM @PlDetail a
    JOIN @PlDetail b
        ON a.uniqueln = b.uniqueln
        AND b.shipdate >= a.shipdate
)
SELECT a.uniqueln
    , SUM(a.qty) AS ordered_running_total
    , SUM(b.shippedqty) AS shipped_running_total
    , a.ship_dts
    , b.shipdate
    , SUM(b.shippedqty) - SUM(a.qty) AS leftover_running_total
FROM DueDates a
JOIN PlDetail b
    ON a.uniqueln = b.uniqueln
    AND a.ship_dts >= b.shipdate
GROUP BY a.uniqueln, a.ship_dts, b.shipdate
HAVING SUM(a.qty) <= SUM(b.shippedqty)
ORDER BY a.uniqueln, a.ship_dts, b.shipdate

关于sql-server - 当 SQL 表不是一对一时,如何计算准时交货,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13961156/

相关文章:

c# - 如何在linq中使用sql函数

SQL - 如果为空,则默认选择

sql-server - 在 WHILE 循环中使用 GETDATE() 总是返回相同的值

sql-server - 基于 key 和初始向量在 SQL Server 中创建对称 AES128 key

sql - 每个查询的最大内存

javascript - 循环通过 api 请求时插入失败仅执行第一个查询

c# - 简单的 20 行选择查询(大多数列是 nvarchar(max))花费太长时间 - 15 秒或更长时间

SQL即使没有子行也返回父行

sql-server-2008 - 如何部署SQL Server Compact Edition 4.0?

sql-server - 如何强制子查询与 #temp 表一样执行?