我试图在SQL中做一个类似Excel的计算,这涉及到使用前一行的收盘率(ClRate)并使用它来计算下一行的值。 这涉及到使用前一行的收盘率(ClRate),并使用它来计算下一行的值。 该表从1月1日开始,有1000行,每行都有已知数据和需要计算的数据(用[ ]表示)。
Date RecQty RecAmt IssQty IssAmt ClQty ClAmt ClRate
1 Jan - - - - 100 $20,000 200
2 Jan +10 +$2100 -5 [ ] [ ] [ ] [ ]
产生所需结果的计算方法如下表所示。
Date RecQty RecAmt IssQty IssAmt ClQty ClAmt ClRate
1 Jan 100 $20,000 200
2 Jan +10 +$2100 -5 -[200*5] [100+10-5] [20,000+2100-200*5] [ClAmt/ClQty]
每天的IssAmt将通过IssQty乘以前几天的ClRate来计算。 ClQty的计算方法是前一天的ClQty+当天的RecQty-当天的IssQty。 ClAmt的计算方法是前一天ClAmt+当日RecAmt-当日IssAmt。最后,每天的ClRate计算为ClAmt ClQty。
唯一已知的ClRate是表的起始库存行(1月1日)--此后每行的ClRate需要计算。
在Excel中,你可以简单地通过连接前一行所需的单元格,并将公式复制粘贴到下面的所有行中来进行计算。
在SQL中,你是如何做到这一点的?我试过自连接CTE,循环和LAG--这些似乎都不行。 原因是从1月2日开始每行的ClRate不知道--虽然Excel可以处理在飞行中使用的计算结果,但SQL无法做到这一点。
求助解决这个问题。我使用的是SQL Server 2017和SSMS。 如果需要,我可以提供代码
表DDL
CREATE TABLE [Auto].[IronOreTbl](
[Id] [int] NULL,
[Counter] [int] NULL,
[TDate] [date] NOT NULL,
[RecQty] [decimal](16, 2) NULL,
[RecAmt] [decimal](16, 2) NULL,
[IssQty] [decimal](16, 2) NULL,
[IssAmt] [decimal](16, 2) NULL,
[ClQty] [decimal](16, 2) NULL,
[ClAmt] [decimal](16, 2) NULL,
[ClRate] [decimal](16, 2) NULL
) ON [PRIMARY]
GO
INSERT INTO [Auto].[IronOreTbl]
([Id],[Counter],[TDate],[RecQty],[RecAmt],[IssQty],[IssAmt],[ClQty],[ClAmt],[ClRate])
VALUES
(1,0,'2019-01-01',NULL,NULL,NULL,NULL,100,20000,200),
(2,1,'2019-01-02',10,2100,5,NULL,105,NULL,NULL),
(3,2,'2019-01-03',8,1600,2,NULL,111,NULL,NULL),
(4,3,'2019-01-04',20,2400,10,NULL,121,NULL,NULL)
试图进行CTE
;WITH ClAmtCTE AS
(
SELECT
Id,RecQty,RecAmt,IssQty,ClQty,ClAmt,ClRate
,EffRate = ClRate
,CumHoldVal= ClAmt
--CAST(ClAmt AS decimal(16,2))
,CumClRt=CAST(ClRate AS decimal(16,2))
,[Counter]
FROM
[Auto].IronOreTbl
WHERE
Id=1
UNION ALL
SELECT
C2.Id,C2.RecQty,c2.RecAmt,C2.IssQty,C2.ClQty,C2.ClAmt,c2.ClRate,
EffRate = (SELECT CumClRt WHERE C2.ID=C2.[Counter]+1),
CumRN =
CAST(
(
CumHoldVal+ISNULL(C2.RecAmt,0)-
(EffRate)*ISNULL(C2.IssQty,0)
)
AS decimal(16,2)
),
CumClRt=CAST(CumHoldVal/C2.ClQty AS decimal(16,2)),
C2.[Counter],
FROM
[Auto].IronOreTbl C2
INNER JOIN ClAmtCTE C1 ON C1.Id = C2.[Counter]
下面的代码达到了预期的结果--虽然我在几天前就已经很接近了,但还是花了这么多时间来完成最后一点;将ClRate与正确的行进行匹配。 还有一个额外的问题,就是在没有问题的日子里,只有收据的日子里,取到的费率属于错误的行(我仍然不知道为什么和如何发生这种情况--我所知道的是,以前的代码需要修改来解决这个差异,修改后的代码也是如此)。
;WITH ClAmtCTE AS
(
SELECT
Id,RecQty,RecAmt,IssQty,ClQty,ClAmt,ClRate
,EffRate = ClRate
,CumHoldVal= ClAmt
--CAST(ClAmt AS decimal(16,2))
,CumClRt=CAST(ClRate AS decimal(16,2))
,[Counter]
FROM
[Auto].IronOreTbl
WHERE
Id=1
UNION ALL
SELECT
C2.Id,C2.RecQty,c2.RecAmt,C2.IssQty,C2.ClQty,C2.ClAmt,c2.ClRate,
EffRate = (SELECT CumClRt WHERE C2.ID=C2.[Counter]+1),
CumRN =
CAST(
(
CumHoldVal+ISNULL(C2.RecAmt,0)-
((SELECT CumClRt WHERE C2.ID=C2.[Counter]+1))*ISNULL(C2.IssQty,0)
)
AS decimal(16,2)
),
CumClRt=CAST((CumHoldVal+ISNULL(C2.RecAmt,0)-
((SELECT CumClRt WHERE C2.ID=C2.[Counter]+1))*ISNULL(C2.IssQty,0))/C2.ClQty AS decimal(16,2)),
C2.[Counter]
FROM
[Auto].IronOreTblC2
INNER JOIN ClAmtCTE C1 ON C1.Id = C2.[Counter]
)
UPDATE [Auto].IronOreTbl
SET ClRate = T1.CumClRt,
ClAmt=CumHoldVal
FROM ClAmtCTE T1
INNER JOIN [Auto].IronOreTbl T2
ON T1.ID = T2.Id
OPTION (MAXRECURSION 0)
尝试,并且在我看来,解决它的过程中,我学到了几件事。 主要包括。
你可以在电子表格中设置和运行的计算 在SQL中是完全不一样的
Excel既能进行即时计算,也能在依赖计算中利用即时计算的结果。 而SQL则不能做到这一点。 在Excel中拥有这种能力意味着你不需要对数据进行迭代--先让Excel计算结果,然后让Excel在需要的地方应用结果。
尽管SQL作为数据存储媒介的用途和作用,但在参加标准的现实世界计算方面还有很长的路要走--比如这个例子;多笔贷款,变化的利率和相关的利息计算等等等等。