在SQL服务器中滑动window函数,高级计算
Sliding window functions in SQL Server, advanced calculation
我有一个问题,例如在 C# 代码中很容易解决,但我不知道如何在 SQL 查询中编写。
情况如下:假设我有一个包含 3 列的 table (ID, Date, Amount
),这里是一些数据:
ID Date Amount
-----------------------
1 01.01.2016 -500
2 01.02.2016 1000
3 01.03.2016 -200
4 01.04.2016 300
5 01.05.2016 500
6 01.06.2016 1000
7 01.07.2016 -100
8 01.08.2016 200
我想从 table 得到的结果是这样的 (ID, Amount .... Order By Date
):
ID Amount
-----------------------
2 300
4 300
5 500
6 900
8 200
我们的想法是将金额分期付款,但问题是当出现负数额时,您需要从最后一期付款中删除金额。我不知道我有多清楚,所以这里有一个例子:
假设我有 3 张发票,金额分别为 500、200、-300。
如果我开始分发这些发票,首先我分发金额 500,然后是 200。但是当我来到第三个 -300 时,我需要从最后一张发票中删除。在其他工作中 200 - 300 = -100,因此第二张发票的金额将消失,但仍有 -100 需要从第一张发票中减去。所以 500 - 100 = 400。我需要的结果是一行数据集(第一张发票金额为 400)
第一张发票金额为负值(-500、300、500)的另一个例子。
在这种情况下,第一个 (-500) 发票将使第二个发票消失,另外 200 个将从第三个发票中减去。所以结果将是:金额为 300 的第三张发票。
这类似于编程语言中的 Stack 实现,但我需要在 SQL 服务器中使用滑动 window 函数来实现。
如果有人有任何想法,请分享。
谢谢。
我用TSQL解决了。但我认为这个任务也可以使用递归 CTE 来解决。
我使用 ID 查找上一行或下一行。
-- create and fill test table
CREATE TABLE Invoices(
ID int,
[Date] date,
Amount float
)
INSERT Invoices(ID,Date,Amount) VALUES
(1,'20160101', -500),
(2,'20160201', 1000),
(3,'20160301', -200),
(4,'20160401', 300),
(5,'20160501', 500),
(6,'20160601', 1000),
(7,'20160701', -100),
(8,'20160801', 200)
我的解决方案
-- copy all the data into temp table
SELECT *
INTO #Invoices
FROM Invoices
DECLARE
@nID int,
@nAmount float,
@pID int
-- run infinity loop
WHILE 1=1
BEGIN
-- set all the variables to NULL
SET @nID=NULL
SET @nAmount=NULL
SET @pID=NULL
-- get data from the last negative row
SELECT
@nID=ID,
@nAmount=Amount
FROM
(
SELECT TOP 1 *
FROM #Invoices
WHERE Amount<0
ORDER BY ID DESC
) q
-- get prev positive row
SELECT @pID=ID
FROM
(
SELECT TOP 1 *
FROM #Invoices
WHERE ID<@nID
AND Amount>0
ORDER BY ID DESC
) q
IF(@pID IS NULL)
BEGIN
-- get next positive row
SELECT @pID=ID
FROM
(
SELECT TOP 1 *
FROM #Invoices
WHERE ID>@nID
AND Amount>0
ORDER BY ID
) q
END
-- exit from loop
IF(@pID IS NULL) BREAK
-- substract amount from positive row
UPDATE #Invoices
SET
Amount+=@nAmount
WHERE ID=@pID
-- delete used negative row
DELETE #Invoices
WHERE ID=@nID
END
-- show result
SELECT *
FROM #Invoices
DROP TABLE #Invoices
我有一个问题,例如在 C# 代码中很容易解决,但我不知道如何在 SQL 查询中编写。
情况如下:假设我有一个包含 3 列的 table (ID, Date, Amount
),这里是一些数据:
ID Date Amount
-----------------------
1 01.01.2016 -500
2 01.02.2016 1000
3 01.03.2016 -200
4 01.04.2016 300
5 01.05.2016 500
6 01.06.2016 1000
7 01.07.2016 -100
8 01.08.2016 200
我想从 table 得到的结果是这样的 (ID, Amount .... Order By Date
):
ID Amount
-----------------------
2 300
4 300
5 500
6 900
8 200
我们的想法是将金额分期付款,但问题是当出现负数额时,您需要从最后一期付款中删除金额。我不知道我有多清楚,所以这里有一个例子:
假设我有 3 张发票,金额分别为 500、200、-300。
如果我开始分发这些发票,首先我分发金额 500,然后是 200。但是当我来到第三个 -300 时,我需要从最后一张发票中删除。在其他工作中 200 - 300 = -100,因此第二张发票的金额将消失,但仍有 -100 需要从第一张发票中减去。所以 500 - 100 = 400。我需要的结果是一行数据集(第一张发票金额为 400)
第一张发票金额为负值(-500、300、500)的另一个例子。 在这种情况下,第一个 (-500) 发票将使第二个发票消失,另外 200 个将从第三个发票中减去。所以结果将是:金额为 300 的第三张发票。
这类似于编程语言中的 Stack 实现,但我需要在 SQL 服务器中使用滑动 window 函数来实现。
如果有人有任何想法,请分享。
谢谢。
我用TSQL解决了。但我认为这个任务也可以使用递归 CTE 来解决。 我使用 ID 查找上一行或下一行。
-- create and fill test table
CREATE TABLE Invoices(
ID int,
[Date] date,
Amount float
)
INSERT Invoices(ID,Date,Amount) VALUES
(1,'20160101', -500),
(2,'20160201', 1000),
(3,'20160301', -200),
(4,'20160401', 300),
(5,'20160501', 500),
(6,'20160601', 1000),
(7,'20160701', -100),
(8,'20160801', 200)
我的解决方案
-- copy all the data into temp table
SELECT *
INTO #Invoices
FROM Invoices
DECLARE
@nID int,
@nAmount float,
@pID int
-- run infinity loop
WHILE 1=1
BEGIN
-- set all the variables to NULL
SET @nID=NULL
SET @nAmount=NULL
SET @pID=NULL
-- get data from the last negative row
SELECT
@nID=ID,
@nAmount=Amount
FROM
(
SELECT TOP 1 *
FROM #Invoices
WHERE Amount<0
ORDER BY ID DESC
) q
-- get prev positive row
SELECT @pID=ID
FROM
(
SELECT TOP 1 *
FROM #Invoices
WHERE ID<@nID
AND Amount>0
ORDER BY ID DESC
) q
IF(@pID IS NULL)
BEGIN
-- get next positive row
SELECT @pID=ID
FROM
(
SELECT TOP 1 *
FROM #Invoices
WHERE ID>@nID
AND Amount>0
ORDER BY ID
) q
END
-- exit from loop
IF(@pID IS NULL) BREAK
-- substract amount from positive row
UPDATE #Invoices
SET
Amount+=@nAmount
WHERE ID=@pID
-- delete used negative row
DELETE #Invoices
WHERE ID=@nID
END
-- show result
SELECT *
FROM #Invoices
DROP TABLE #Invoices