3

我有一张包含库存交易的表格。一个简化的例子:

--Inventory Transactions            
Date         Sold    Purchased  Balance(not in table)
Today        1                  -5
Yesterday    6                  -4
5 days ago           5          +2
10 days ago  103                -3 
20 days ago          100        +100

要求表明报告应包含自文章出现负余额(缺货)以来的日期。在上面的例子中,它意味着昨天作为答案。

我正在尝试将其转换为 SQL,但遇到了一些麻烦。我尝试过使用 CTE:

with Stockouts as (
select getdate() as [Date],
       (calculation) as Balance
from [Inventory Transactions]
--some constraints to get the correct article are omitted
union all
select dateadd(dd, -1, Stockouts.[Date]) as [Date], 
       Stockouts.Balance - (calculation) as Balance
from [Inventory Transactions]
inner join Stockouts    
)

但是有一个问题是我不能在递归部分使用子查询(查找当前事务之前的最后一个事务),并且当某个日期没有事务时,内部连接将停止循环(因此该dateadd部分也会失败) )。

解决此问题的最佳方法是什么?

4

2 回答 2

2

我认为最好的方法是这样使用OUTER APPLY

DECLARE @InventoryTransactions TABLE ([Date] DATE, Sold INT, Purchased INT)
INSERT @InventoryTransactions VALUES 
    ('20120504', 1, 0),
    ('20120503', 6, 0),
    ('20120501', 0, 5),
    ('20120425', 103, 0),
    ('20120415', 0, 100)

SELECT  trans.Date, 
        trans.Sold, 
        trans.Purchased, 
        ISNULL(Balance, 0) [BalanceIn],
        ISNULL(Balance, 0) + (Purchased - Sold) [BalanceOut]
FROM    @InventoryTransactions trans
        OUTER APPLY
        (   SELECT  SUM(Purchased - Sold) [Balance]
            FROM    @InventoryTransactions bal
            WHERE   Bal.Date < trans.Date
        ) bal

您的方法不太适合递归。如果您需要所有日期,那么最好创建一个日期表并将LEFT JOIN上面的结果创建到包含所有日期的表中。最好有一个永久的日期表(例如 dbo.Calendar),因为它们可以在多种情况下使用,但您始终可以使用循环、CTE 或系统视图来创建临时表来操作它。关于如何生成递增日期列表的问题之前已经回答过

编辑

只需重新阅读您的要求,我认为这是获得您真正想要的东西的更好方法(使用相同的示例数据)。

;WITH Transactions AS
(   SELECT  trans.Date, 
            trans.Sold, 
            trans.Purchased, 
            ISNULL(Balance, 0) [BalanceIn],
            ISNULL(Balance, 0) + (Purchased - Sold) [BalanceOut]
    FROM    @InventoryTransactions trans
            OUTER APPLY
            (   SELECT  SUM(Purchased - Sold) [Balance]
                FROM    @InventoryTransactions bal
                WHERE   Bal.Date < trans.Date
            ) bal
) 
SELECT  DATEDIFF(DAY, MAX(Date), CURRENT_TIMESTAMP) [Days Since Negative Balance]
FROM    Transactions
WHERE   BalanceIn > 0

编辑 2

我创建了一个SQL Fiddle来演示查询计划OUTER APPLY和递归之间的区别。您可以看到 CTE 的工作量更大,并且当在我的本地计算机上运行相同的数据时,它告诉我,当在同一批次中运行这两个时,外部应用方法的相对批次成本为 17%,不到四分之一递归 CTE 方法占 83%。

于 2012-05-04T09:47:52.743 回答
1

如果您想在递归 cte 中执行此操作。这可能是一个建议:

测试数据:

DECLARE @T TABLE(Date DATETIME,Sold INT, Purchased INT)

INSERT INTO @T
VALUES
    (GETDATE(),1,NULL),
    (GETDATE()-1,6,NULL),
    (GETDATE()-5,NULL,5),
    (GETDATE()-10,103,NULL),
    (GETDATE()-20,NULL,100)

询问

;WITH CTE
AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY Date ASC) AS RowNbr, t.* FROM @T AS T
)
, CTE2
AS
(
    SELECT
        CTE.RowNbr,
        CTE.Date,
        CTE.Sold,
        CTE.Purchased,
        (ISNULL(CTE.Purchased,0)-ISNULL(CTE.Sold,0)) AS Balance
    FROM
        CTE
    WHERE
         CTE.RowNbr=1
    UNION ALL
    SELECT
        CTE.RowNbr,
        CTE.Date,
        CTE.Sold,
        CTE.Purchased,
        CTE2.Balance+ISNULL(CTE.Purchased,0)-ISNULL(CTE.Sold,0) AS Balance
    FROM
        CTE
        JOIN CTE2
            ON CTE.RowNbr=CTE2.RowNbr+1
)
SELECT * FROM CTE2 ORDER BY CTE2.RowNbr DESC

输出

5   2012-05-04 11:49:45.497 1       NULL    -5
4   2012-05-03 11:49:45.497 6       NULL    -4
3   2012-04-29 11:49:45.497 NULL    5        2
2   2012-04-24 11:49:45.497 103     NULL    -3
1   2012-04-14 11:49:45.497 NULL    100     100
于 2012-05-04T09:53:07.683 回答