如何根据 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_rb 和 pre_ra 中的方式编写它在计算后更新为上一行的 post_ra 和 post_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 中出现由于选择不明确的值而导致的数学错误,这是可以避免的,但每个值只需要引用 r
或 d
值,而不是先前的值计算“同一行”的中间值
所以有了这个数据:
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);
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
我正在尝试为 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_rb 和 pre_ra 中的方式编写它在计算后更新为上一行的 post_ra 和 post_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 中出现由于选择不明确的值而导致的数学错误,这是可以避免的,但每个值只需要引用 r
或 d
值,而不是先前的值计算“同一行”的中间值
所以有了这个数据:
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);
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 |