排名在 MySQL 的加权总和

Weighted SUM with rank in MySQL

我一直在尝试计算 table 的每个用户的总和,当总和根据某种顺序由每行的排名加权时。这是在视频游戏的上下文中,最高分值 100%,第二名值 95%,第三名值 (0.95 * 0.95) * 100% 等等。

公式可以简单描述为SUM(score * POW(0.95, score's position)),但我尝试的一切似乎都达到了MySQL的限制。

首先这个选项不起作用,因为 ORDER BY pls.Score DESC 似乎是在 SUM 之后调用的(SUM 似乎没有通过改变顺序而改变)。

SELECT p.PlayerId, p.Username, 
        (SELECT SUM(pls.Score * POW(0.95, @i := if(@c = p.PlayerId, @i + 1, if(@c := p.PlayerId, 0, 0))))
        FROM player_level_stats pls, (SELECT @i := -1, @c := '') params
        WHERE pls.PlayerId = p.PlayerId
        ORDER BY pls.Score DESC) * 10 AS Score
FROM player p
ORDER BY 3 DESC;

我还必须使用这个混乱的变量,因为我不能在 SUM().

中包含 ROW_NUMBER() OVER ()

现在好了,我接下来要尝试的是在外部查询中求和,这样我就可以在求和之前进行排序:

SELECT p.PlayerId, p.Username, 
        (SELECT SUM(t.Score)
        FROM (SELECT pls.Score * POW(0.95, @i := if(@c = p.PlayerId, @i + 1, if(@c := p.PlayerId, 0, 0)))
            FROM player_level_stats pls, (SELECT @i := -1, @c := '') params
            WHERE pls.PlayerId = p.PlayerId
            ORDER BY pls.Score DESC) AS t) * 10 AS Score
FROM player p
ORDER BY 3 DESC
LIMIT 50;

理论上听起来不错,但在实践中有一个 MySQL 限制,您不能将参数传递给嵌套子查询(关于这个的回答基本上告诉您尝试其他方法)。由于嵌套子查询在 FROM 中,似乎很难将某些内容传递给中间查询。

当然总会有慢的 SUM(pls.Score * POW(0.95, (SELECT COUNT(*) FROM player_level_stats ipls WHERE pls.Score > ipls.Score))) 但这是 O(分数数 ^ 2) 而不是 O(分数数 * log(分数数)) 使整个事情慢得多.

在 MySQL 中有什么方法可以做到这一点吗?或者有没有办法用 MariaDB 做到这一点?

编辑: 这是一个最小版本(也不起作用)

SELECT p.PlayerId, (SELECT SUM(pls.Score * (RANK() OVER (ORDER BY pls.Score DESC)))
        FROM player_level_stats pls
        WHERE pls.PlayerId = p.PlayerId
        ORDER BY pls.Score DESC) AS WeightedScore
FROM player p;

这只是将每个玩家的分数乘以该分数的排名(最好分数 * 1 + 第二好分数 * 2 + ... n 最好分数 * n)。这本质上是同一个问题,但经过了简化。

如果我对问题的理解正确,你可以做的是对总和进行子查询,然后使用 row_number() over(order by) 来获得排名。像这样:

SELECT playerID, SUM(score * weighted) AS totalscore
FROM
(
 SELECT 
  playerID, score, POW(0.95,(ROW_NUMBER() OVER(partition by playerID ORDER BY score desc)-1)) as weighted
  FROM player_level_stats
 ) AS totals
GROUP BY playerID