Gre*_*Gum 3 sql sql-server common-table-expression
我有一个这样的表:
Year, DividendYield
1950, .1
1951, .2
1952, .3
Run Code Online (Sandbox Code Playgroud)
我现在想计算总运行份额。换句话说,如果股息重新投资于新股,现在看起来会像这样:
1950 年 1 月 1 日购买的原始股份数量为 1
1950, .1, 1.1 -- yield of .1 reinvested in new shares results in .1 new shares, totaling 1.1
1951, .2, 1.32 -- (1.1 (Prior Year Total shares) * .2 (dividend yield) + 1.1 = 1.32)
1953, .3, 1.716 -- (1.32 * .3 + 1.32 = 1.716)
Run Code Online (Sandbox Code Playgroud)
我能想到的最接近的是:
declare @startingShares int = 1
; with cte_data as (
Select *,
@startingShares * DividendYield as NewShares,
(@startingShares * DividendYield) + @startingShares as TotalShares from DividendTest
)
select *, Sum(TotalShares) over (order by id) as RunningTotal from cte_data
Run Code Online (Sandbox Code Playgroud)
但只有第一行是正确的。
Id Year DividendYield NewShares TotalShares RunningTotal
1 1950 0.10 0.10 1.10 1.10
2 1951 0.20 0.20 1.20 2.30
3 1953 0.30 0.30 1.30 3.60
Run Code Online (Sandbox Code Playgroud)
我如何使用 SQL 执行此操作?我试图不诉诸循环来处理这个问题。
你想要一个累积乘法。我认为相关 CTE 实际上是最简单的解决方案:
with tt as (
select t.*, row_number() over (order by year) as seqnum
from t
),
cte as (
select tt.year, convert(float, tt.yield) as yield, tt.seqnum
from tt
where seqnum = 1
union all
select tt.year, (tt.yield + 1) * (cte.yield + 1) - 1, tt.seqnum
from cte join
tt
on tt.seqnum = cte.seqnum + 1
)
select cte.*
from cte;
Run Code Online (Sandbox Code Playgroud)
这是一个 db<>fiddle。
您还可以使用对数和指数来表述:
select t.*,
exp(sum(log(1 + yield)) over (order by year)) - 1
from t;
Run Code Online (Sandbox Code Playgroud)
这对于大多数用途来说应该没问题,但我发现对于较长的序列,这比递归 CTE 更快地引入数值错误。