这可以使用窗口聚合以基于集合的方式完成,以构建运行总计。根据表中的行数,这可能会有点慢,但您不能真正限制向后追溯的时间范围,因为最后一个正日期是未知数量。
为方便起见,我使用 CTE 来构建聚合数据集,但将其转换为临时表应该更快。 (每次调用 CTE 时都会执行一次,而临时表只会执行一次。)
基本理论是使用OVER 子句对SUM 聚合进行分区和排序,从而构建所有前几天的运行总计。然后使用该数据集并将其过滤到预期日期。当该表中的一行的数量小于零时,它将重新连接到该产品和数量大于零的位置的所有前几天的聚合数据集。
由于这可能会返回多个正日期行,因此ROW_NUMBER() 函数用于根据正数日期的日期对行进行排序。这是按降序完成的,因此第 1 行是最近的正数日。此处无法使用简单的MIN(),因为MIN([Date]) 可能与MIN(Quantity) 不对应。
WITH x AS (
SELECT [Date],
Product,
[Location],
SUM(Quantity) OVER (PARTITION BY Product, [Location] ORDER BY [Date] ASC) AS Quantity,
SUM([Value]) OVER(PARTITION BY Product, [Location] ORDER BY [Date] ASC) AS [Value]
FROM ProductTransactions
WHERE [Date] <= @AsAtDate
)
SELECT [Date], Product, [Location], Quantity, [Value], Positive_date, Positive_date_quantity
FROM (
SELECT x1.[Date], x1.Product, x1.[Location], x1.Quantity, x1.[Value],
x2.[Date] AS Positive_date, x2.[Quantity] AS Positive_date_quantity,
ROW_NUMBER() OVER (PARTITION BY x1.Product, x1.[Location] ORDER BY x2.[Date] DESC) AS Positive_date_row
FROM x AS x1
LEFT JOIN x AS x2 ON x1.Product=x2.Product AND x1.[Location]=x2.[Location]
AND x2.[Date]<x1.[Date] AND x1.Quantity<0 AND x2.Quantity>0
WHERE x1.[Date] = @AsAtDate
) AS y
WHERE Positive_date_row=1