更新 #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。这允许您逐行迭代 tpxvx 的计算:

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

Demo on SQLFiddle