T-SQL:在今天之前找到聚合状态的最后一次出现

时间:2012-05-04 09:29:45

标签: sql-server-2008

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

--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    
)

但是问题是我不能在递归部分中使用子查询(在当前事件之前查找最后一个事务),并且当某个日期没有事务时,内部联接将停止循环(所以{{ 1}}部分也会失败。)

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

2 个答案:

答案 0 :(得分: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或系统视图来创建临时表来操作它。关于如何generate a list of incrementing dates之前已经回答的问题

修改

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

;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和Recursion之间查询计划的差异。你可以看到CTE的质量更大,当在我的本地机器上运行相同的数据时,它告诉我,当在同一批次中运行这两个数据时,外部应用方法的相对批量成本比不到四分之一的其中83%采用递归CTE方法。

答案 1 :(得分: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