如何根据 Snowflake 中的先前行递归计算行?

How to recursively calculate rows based on previous rows in Snowflake?

我正在尝试为 table 中的每个玩家建立一个表现分数,并将其用于 运行 一些分析。我曾尝试使用 Elo 排名,使用玩家在游戏中的得分与之前游戏中的自己的得分。该游戏是一场计时赛,所以他们完成得越快,他们的表现就越好。我模拟 win (1) 当当前比赛比他们之前的比赛快时, loss (0) 当它慢时 draw(.5)时差为0.

Elo 的算法有两个组成部分...所有玩家的结果估计(在我的例子中是两个 - Ea、Eb)和排名更新(Ra、Rb)。

我有当前设置来模拟一次计算

select
    player_id,
    race_ind,
    k,
    sa,
    sb,
    pre_ra,
    pre_rb,
    div0(1, 1 + pow(10,(pre_rb-pre_ra)/400)) ea,
    div0(1, 1 + pow(10,(pre_ra-pre_rb)/400)) eb,
    round(pre_ra + k * (sa-ea)) post_ra,
    round(pre_rb + k * (sb-eb)) post_rb
from
    t

下面是return。

player_id race_ind k sa sb pre_ra pre_rb ea eb post_ra post_rb
xxxxx 1 24 1 0 1000 1000 0.5 0.5 1012 988
xxxxx 2 24 1 0 null null null null null null
xxxxx 3 24 0 1 null null null null null null
xxxxx 4 24 1 0 null null null null null null

但我需要以在后续行 pre_rbpre_ra 中的方式编写它在计算后更新为上一行的 post_rapost_rb 所以结果应该 return 这个 table 而不是

player_id race_ind k sa sb pre_ra pre_rb ea eb post_ra post_rb
xxxxx 1 24 1 0 1000 1000 0.5 0.5 1012 988
xxxxx 2 24 1 0 1012 988 0.5344839447 0.4655160553 1023 977
xxxxx 3 24 0 1 1023 977 0.5658152031 0.4341847969 1009 991
xxxxx 4 24 1 0 1009 991 0.5258809309 0.4741190691 1020 980

所以收获一点,这个过程计算的值是没有意义的。这是你如何做到的。我精简了公式,以避免在 CTE 中出现由于选择不明确的值而导致的数学错误,这是可以避免的,但每个值只需要引用 rd 值,而不是先前的值计算“同一行”的中间值

所以有了这个数据:


create table data (player_id number, race_ind number, k float, sa float);

insert into data VALUES 
    (100, 1, 24, 1),
    (100, 2, 24, 1),
    (100, 3, 24, 0),
    (100, 4, 24, 1);

这个recursive CTE

with RECURSIVE rec_cte as (
    select 
        d.player_id, 
        d.race_ind, 
        d.sa, 
        round(1000::float + k * (d.sa - div0(1, 1 + pow(10,(0::float )/400)))) post_ra,
        round(1000::float + k * (1-d.sa - div0(1, 1 + pow(10,(0::float )/400)))) post_rb
    from data as d
    where race_ind = 1

    UNION ALL

    select 
        d.player_id, 
        d.race_ind, 
        d.sa, 
        round(r.post_ra + k::float * (d.sa-div0(1, 1 + pow(10,(r.post_rb-r.post_ra)/400)))) as post_ra,
        round(r.post_rb + k::float * (1-d.sa-div0(1, 1 + pow(10,(r.post_ra-r.post_rb)/400)))) as post_rb
    from rec_cte as r
    join data as d 
    where r.player_id = d.player_id and r.race_ind + 1  = d.race_ind
)
select *
from rec_cte

给出:

PLAYER_ID RACE_IND SA POST_RA POST_RB
100 1 1 1,012 988
100 2 1 1,023 977
100 3 0 1,009 991
100 4 1 1,020 980