T-SQL 无循环迭代计算
T-SQL iterative calculation without loop
我有以下财务数据,我需要显示截至日期的余额。
我在下面的逻辑中使用了循环,但我想在没有循环或游标的情况下使用单个查询来实现同样的事情。对此的任何想法都会有所帮助。
DROP TABLE IF EXISTS #Transact;
CREATE TABLE #Transact
(
dt DATE,
Transact VARCHAR(1),
Amount INT,
Balance INT
)
INSERT INTO #Transact
(
dt,
Transact,
Amount
)
SELECT *
FROM
(
VALUES
('01-01-2022', 'C', 1000),
('02-01-2022', 'C', 1000),
('03-01-2022', 'D', 1000),
('04-01-2022', 'C', 2000),
('05-01-2022', 'C', 2000),
('06-01-2022', 'D', 2000),
('07-01-2022', 'D', 2000),
('08-01-2022', 'C', 3000),
('09-01-2022', 'C', 1500),
('10-01-2022', 'D', 1500),
('11-01-2022', 'D', 1500),
('12-01-2022', 'C', 1500)
) VTE (DT, Transact, Amount);
declare @interval date = '01-01-2022',
@balance int
WHILE (@interval <= (select max(dt) FROM #Transact))
BEGIN
;WITH CTE
AS (SELECT *,
LAG(Balance) Over (Order by dt) AS PreBalance
FROM #Transact
)
UPDATE CTE
SET Balance = CASE
WHEN Transact = 'C' THEN
Amount + ISNULL(PreBalance, 0)
ELSE
ISNULL(PreBalance, 0) - Amount
END
WHERE dt = @interval
SET @interval = DATEADD(DAY, 1, @interval)
SELECT @balance = Balance
from #Transact
where dt = @interval
END
SELECT * FROM #Transact;
我有大量这样的数据,我的要求是避免游标或循环进行此计算。
我认为这是一个 运行 总数,所以你需要一个窗口 SUM()
和一个 CASE
表达式:
SELECT
dt, Transact, Amount,
SUM(
CASE
WHEN Transact = 'C' THEN Amount
WHEN Transact = 'D' THEN -Amount
ELSE 0
END) OVER (ORDER BY dt) AS Balance
FROM #Transact
您是否尝试过像这样的简单查询:
SELECT
(
SELECT SUM(Amount) FROM #Transact WHERE Transact = 'C' and dt <= @interval
)
-
(
SELECT SUM(Amount) FROM #Transact WHERE Transact = 'D' and dt <= @interval
)
as Balance
基本上,您只需计算所有贷项并减去所有借项,直到日期为止。
或者,按照@Zhorov 的建议:
SELECT SUM
(
CASE
WHEN Transact = 'C' THEN Amount
WHEN Transact = 'D' THEN -Amount
ELSE 0
END
) as Balance
FROM #Transact WHERE dt <= @interval
我有以下财务数据,我需要显示截至日期的余额。 我在下面的逻辑中使用了循环,但我想在没有循环或游标的情况下使用单个查询来实现同样的事情。对此的任何想法都会有所帮助。
DROP TABLE IF EXISTS #Transact;
CREATE TABLE #Transact
(
dt DATE,
Transact VARCHAR(1),
Amount INT,
Balance INT
)
INSERT INTO #Transact
(
dt,
Transact,
Amount
)
SELECT *
FROM
(
VALUES
('01-01-2022', 'C', 1000),
('02-01-2022', 'C', 1000),
('03-01-2022', 'D', 1000),
('04-01-2022', 'C', 2000),
('05-01-2022', 'C', 2000),
('06-01-2022', 'D', 2000),
('07-01-2022', 'D', 2000),
('08-01-2022', 'C', 3000),
('09-01-2022', 'C', 1500),
('10-01-2022', 'D', 1500),
('11-01-2022', 'D', 1500),
('12-01-2022', 'C', 1500)
) VTE (DT, Transact, Amount);
declare @interval date = '01-01-2022',
@balance int
WHILE (@interval <= (select max(dt) FROM #Transact))
BEGIN
;WITH CTE
AS (SELECT *,
LAG(Balance) Over (Order by dt) AS PreBalance
FROM #Transact
)
UPDATE CTE
SET Balance = CASE
WHEN Transact = 'C' THEN
Amount + ISNULL(PreBalance, 0)
ELSE
ISNULL(PreBalance, 0) - Amount
END
WHERE dt = @interval
SET @interval = DATEADD(DAY, 1, @interval)
SELECT @balance = Balance
from #Transact
where dt = @interval
END
SELECT * FROM #Transact;
我有大量这样的数据,我的要求是避免游标或循环进行此计算。
我认为这是一个 运行 总数,所以你需要一个窗口 SUM()
和一个 CASE
表达式:
SELECT
dt, Transact, Amount,
SUM(
CASE
WHEN Transact = 'C' THEN Amount
WHEN Transact = 'D' THEN -Amount
ELSE 0
END) OVER (ORDER BY dt) AS Balance
FROM #Transact
您是否尝试过像这样的简单查询:
SELECT
(
SELECT SUM(Amount) FROM #Transact WHERE Transact = 'C' and dt <= @interval
)
-
(
SELECT SUM(Amount) FROM #Transact WHERE Transact = 'D' and dt <= @interval
)
as Balance
基本上,您只需计算所有贷项并减去所有借项,直到日期为止。
或者,按照@Zhorov 的建议:
SELECT SUM
(
CASE
WHEN Transact = 'C' THEN Amount
WHEN Transact = 'D' THEN -Amount
ELSE 0
END
) as Balance
FROM #Transact WHERE dt <= @interval