我有一张包含库存交易的表格。一个简化的例子:
--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}}部分也会失败。)
解决此问题的最佳方法是什么?
答案 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