更新 #1 - 如何引用 SQL 服务器中计算列的先前值?
Update #1 - How to refer to the previous value of a computed column in SQL Server?
Disclaimer: originally posted on DBA
更新 #2
Fiddle 根据@Nick 的观察更新:http://sqlfiddle.com/#!18/e0ef2c/2
更新 #1
我能够使用用户定义的标量函数对我的案例进行一些改进:
create function [dbo].[tpx]
(
@qx numeric(15, 10)
)
returns numeric(15, 10)
as
begin
declare @tpx as numeric(15, 10) = 1;
return @tpx * ( 1 - @qx );
end;
出于某种原因我无法更新我的 fiddle,但我的查询现在是这样的:
use GESTAO_ATUARIAL;
select
case when row_number() over (order by id) = 1 then
1
else
lag(tpx, 1, 1) over (order by id) * (1 - lag(qx, 1) over (order by id))
end as tpx_final
from (
select
t.id,
t.ben,
t.mensalizacao,
t.qx,
(lag(dbo.tpx(qx), 1, 1) over (order by id)) as tpx,
t.vx,
t.valor_ben
from (
select
aux.Id as id,
dbo.CalcularIdade(aux.[DataNascimento], getdate()) + row_number() over (order by aux.Id) - 1 as idade,
case when aux.Sexo = 'M' and aux.CondicaoParticipacao = 'N' then
cast(mrt.Masculino as numeric(7,5))
when aux.Sexo = 'M' and aux.CondicaoParticipacao = 'S' then
inv.Masculino
when aux.Sexo = 'F' and aux.CondicaoParticipacao = 'S' then
inv.Feminino
else
mrt.Feminino
end as qx,
case when (row_number() over (order by aux.Id)) = 1 then
1
else
(1 / power( 1 + .03370, row_number() over (order by aux.Id) - 1))
end as vx,
case when aux.Sexo = 'M' then
men.FatorMensalizacaoMasculino
else
men.FatorMensalizacaoFeminino
end as mensalizacao,
cast(aux.ValorBeneficio * ( 1 + 0.03284) as numeric(10,2)) as valor_ben,
aux.TipoBeneficio as ben
from Mortalidade mrt
left join AuxilioAlimentacaoParticipantes aux
on mrt.Idade >= dbo.CalcularIdade(aux.[DataNascimento], getdate())
left join MortalidadeInvalidos inv
on mrt.Idade = inv.Idade
left join FatorMensalizacao men
on mrt.Idade = men.Idade
where aux.Id = 1
) as t
) as s
但是结果还是有点偏差,前三行还可以,后面几行就完全不对了
此时我真的不知道这是否是在 SQL 服务器中解决此问题的正确方法。
结果:
应该是什么:
原创
我正在尝试将我的 IAS 19 工具从 MS Excel 转移到 MS SQL 服务器。
昨天,我取得了重大进展,但当我认为我已经完全成功时,我陷入了一个问题,这个问题需要比我在 SQL 服务器上的经验多一点。
为了根据 IAS 19 计算义务,我们必须使用 life tables,它显示每个年龄段“该年龄段的人将在他或她的下一个生日之前死亡”的概率(维基百科)。这些概率是按男性或女性分开的。
我有一个table,其中包含一些福利计划参与者的数据,还有生命table,其中为从 0 到 120 岁的每个年龄段计算了 q(x) .
基本上,当我需要计算一个人的义务时,比方说,我必须在模拟中将这个人从 54 岁增加到 120 岁。如果这个人是 25 岁,则从 25 岁增加到 120 岁. 总而言之,我必须让这个人从他们现在的年龄走到生命的最后一个年龄table(如果我在这里不够清楚,请告诉我)。
我通过对左侧的生命 table 进行 LEFT JOIN 来实现此结果。为了使年龄增加一,我使用了以下方法,这样年龄就不会冻结在当前年龄:
dbo.ComputeAge(bp.[BirthDate], getdate()) + row_number() over (order by bp.Id) - 1 as age
我无法一次计算我数据库中的每个人(约 15,000 行)。我还没到这个阶段呢
我的问题是我还必须计算一个名为 tpx 的变量,它直接取决于它的先前值和 q(x) 的先前值。
对于第一行,与受益人的年龄无关,tpx 始终为 1,然后使用它的先前值和 q(x) 的先前值计算。
举个例子:
男性54岁:q(x),根据寿命table,为0.0025568。
2020 年 tpx 为 1
。在 2021 年,tpx 是 tpx[t-1 = 2020] * ( 1- q(x)[t-1 = 2020])
我第一次尝试计算 tpx
是
case when (row_number() over (order by id)) = 1 then
1
else
lag(tpx, 1) over (order by id) * (1 - lag(qx, 1) over (order by id))
end as tpx
我为此准备了一个fiddle:http://sqlfiddle.com/#!18/bae0a/6
另外,抱歉我的英语不好。
可能更简单的实现方法是使用递归 CTE。这允许您逐行迭代 tpx
和 vx
的计算:
WITH cte AS (
SELECT lt.Age,
bp.Gender,
CASE WHEN bp.Gender = 'M' THEN CAST(lt.Male as NUMERIC(7,5))
ELSE CAST(lt.Female as NUMERIC(7,5))
END AS qx,
CAST(1.0 AS NUMERIC(7,5)) AS tpx,
CAST(1.0 AS NUMERIC(7,5)) AS VX
FROM LifeTable lt
LEFT JOIN BenefitPlanParticipants bp on lt.Age = dbo.ComputeAge(bp.[BirthDate], GETDATE())
WHERE bp.Id = 1
UNION ALL
SELECT cte.Age + 1,
Gender,
CASE WHEN Gender = 'M' THEN CAST(lt.Male as NUMERIC(7,5))
ELSE CAST(lt.Female AS NUMERIC(7,5))
END,
CAST(tpx * (1.0 - qx) AS NUMERIC(7,5)),
CAST(vx / 1.03370 AS NUMERIC(7,5))
FROM LifeTable lt
JOIN cte ON cte.Age + 1 = lt.Age
WHERE cte.Age < 120
)
SELECT *
FROM cte
输出(仅前几行):
Age Gender qx tpx VX
54 M 0.00256 1 1
55 M 0.0029 0.99744 0.9674
56 M 0.00336 0.99455 0.93586
57 M 0.00375 0.99121 0.90535
58 M 0.00422 0.98749 0.87583
59 M 0.00476 0.98332 0.84728
60 M 0.0054 0.97864 0.81966
Disclaimer: originally posted on DBA
更新 #2
Fiddle 根据@Nick 的观察更新:http://sqlfiddle.com/#!18/e0ef2c/2
更新 #1
我能够使用用户定义的标量函数对我的案例进行一些改进:
create function [dbo].[tpx]
(
@qx numeric(15, 10)
)
returns numeric(15, 10)
as
begin
declare @tpx as numeric(15, 10) = 1;
return @tpx * ( 1 - @qx );
end;
出于某种原因我无法更新我的 fiddle,但我的查询现在是这样的:
use GESTAO_ATUARIAL;
select
case when row_number() over (order by id) = 1 then
1
else
lag(tpx, 1, 1) over (order by id) * (1 - lag(qx, 1) over (order by id))
end as tpx_final
from (
select
t.id,
t.ben,
t.mensalizacao,
t.qx,
(lag(dbo.tpx(qx), 1, 1) over (order by id)) as tpx,
t.vx,
t.valor_ben
from (
select
aux.Id as id,
dbo.CalcularIdade(aux.[DataNascimento], getdate()) + row_number() over (order by aux.Id) - 1 as idade,
case when aux.Sexo = 'M' and aux.CondicaoParticipacao = 'N' then
cast(mrt.Masculino as numeric(7,5))
when aux.Sexo = 'M' and aux.CondicaoParticipacao = 'S' then
inv.Masculino
when aux.Sexo = 'F' and aux.CondicaoParticipacao = 'S' then
inv.Feminino
else
mrt.Feminino
end as qx,
case when (row_number() over (order by aux.Id)) = 1 then
1
else
(1 / power( 1 + .03370, row_number() over (order by aux.Id) - 1))
end as vx,
case when aux.Sexo = 'M' then
men.FatorMensalizacaoMasculino
else
men.FatorMensalizacaoFeminino
end as mensalizacao,
cast(aux.ValorBeneficio * ( 1 + 0.03284) as numeric(10,2)) as valor_ben,
aux.TipoBeneficio as ben
from Mortalidade mrt
left join AuxilioAlimentacaoParticipantes aux
on mrt.Idade >= dbo.CalcularIdade(aux.[DataNascimento], getdate())
left join MortalidadeInvalidos inv
on mrt.Idade = inv.Idade
left join FatorMensalizacao men
on mrt.Idade = men.Idade
where aux.Id = 1
) as t
) as s
但是结果还是有点偏差,前三行还可以,后面几行就完全不对了
此时我真的不知道这是否是在 SQL 服务器中解决此问题的正确方法。
结果:
应该是什么:
原创
我正在尝试将我的 IAS 19 工具从 MS Excel 转移到 MS SQL 服务器。
昨天,我取得了重大进展,但当我认为我已经完全成功时,我陷入了一个问题,这个问题需要比我在 SQL 服务器上的经验多一点。
为了根据 IAS 19 计算义务,我们必须使用 life tables,它显示每个年龄段“该年龄段的人将在他或她的下一个生日之前死亡”的概率(维基百科)。这些概率是按男性或女性分开的。
我有一个table,其中包含一些福利计划参与者的数据,还有生命table,其中为从 0 到 120 岁的每个年龄段计算了 q(x) .
基本上,当我需要计算一个人的义务时,比方说,我必须在模拟中将这个人从 54 岁增加到 120 岁。如果这个人是 25 岁,则从 25 岁增加到 120 岁. 总而言之,我必须让这个人从他们现在的年龄走到生命的最后一个年龄table(如果我在这里不够清楚,请告诉我)。
我通过对左侧的生命 table 进行 LEFT JOIN 来实现此结果。为了使年龄增加一,我使用了以下方法,这样年龄就不会冻结在当前年龄:
dbo.ComputeAge(bp.[BirthDate], getdate()) + row_number() over (order by bp.Id) - 1 as age
我无法一次计算我数据库中的每个人(约 15,000 行)。我还没到这个阶段呢
我的问题是我还必须计算一个名为 tpx 的变量,它直接取决于它的先前值和 q(x) 的先前值。
对于第一行,与受益人的年龄无关,tpx 始终为 1,然后使用它的先前值和 q(x) 的先前值计算。
举个例子:
男性54岁:q(x),根据寿命table,为0.0025568。
2020 年 tpx 为 1
。在 2021 年,tpx 是 tpx[t-1 = 2020] * ( 1- q(x)[t-1 = 2020])
我第一次尝试计算 tpx
是
case when (row_number() over (order by id)) = 1 then
1
else
lag(tpx, 1) over (order by id) * (1 - lag(qx, 1) over (order by id))
end as tpx
我为此准备了一个fiddle:http://sqlfiddle.com/#!18/bae0a/6
另外,抱歉我的英语不好。
可能更简单的实现方法是使用递归 CTE。这允许您逐行迭代 tpx
和 vx
的计算:
WITH cte AS (
SELECT lt.Age,
bp.Gender,
CASE WHEN bp.Gender = 'M' THEN CAST(lt.Male as NUMERIC(7,5))
ELSE CAST(lt.Female as NUMERIC(7,5))
END AS qx,
CAST(1.0 AS NUMERIC(7,5)) AS tpx,
CAST(1.0 AS NUMERIC(7,5)) AS VX
FROM LifeTable lt
LEFT JOIN BenefitPlanParticipants bp on lt.Age = dbo.ComputeAge(bp.[BirthDate], GETDATE())
WHERE bp.Id = 1
UNION ALL
SELECT cte.Age + 1,
Gender,
CASE WHEN Gender = 'M' THEN CAST(lt.Male as NUMERIC(7,5))
ELSE CAST(lt.Female AS NUMERIC(7,5))
END,
CAST(tpx * (1.0 - qx) AS NUMERIC(7,5)),
CAST(vx / 1.03370 AS NUMERIC(7,5))
FROM LifeTable lt
JOIN cte ON cte.Age + 1 = lt.Age
WHERE cte.Age < 120
)
SELECT *
FROM cte
输出(仅前几行):
Age Gender qx tpx VX
54 M 0.00256 1 1
55 M 0.0029 0.99744 0.9674
56 M 0.00336 0.99455 0.93586
57 M 0.00375 0.99121 0.90535
58 M 0.00422 0.98749 0.87583
59 M 0.00476 0.98332 0.84728
60 M 0.0054 0.97864 0.81966