MySQL select 语句 - 如何计算当前排名

Posted

技术标签:

【中文标题】MySQL select 语句 - 如何计算当前排名【英文标题】:MySQL select statement - How to calculate the current ranking 【发布时间】:2014-12-10 06:24:16 【问题描述】:

我有一个名为 user_rankings 的表,其中存储了每个用户的投票 (voted)。我想显示当前用户的排名(本周),这取决于用户获得了多少票。

举例说明:

RANK-NR、用户名、已投票 1, name1, 18 次 2, name1, 16 次 (我的排名在这里),我的名字,13次

在这个例子中,我的排名应该是 3。如果我有 17 票,我会是第 2 名。如果我上面有 5 个用户,我会是第 8 名。我想你明白了。

现在我可以在 php 中使用递增的$i 轻松显示排名数字。但是我只想显示一个限制为十个用户的列表(前十名列表),然后直接显示我当前的排名,如果我还没有进入前十名的话。所以我只是想知道如何使用 mysql 获得我的确切排名。 我假设这个列表中有数百个用户,他们的票数不同。

这是我此刻的声明:

SELECT 
`voted`
FROM `users_ranking` 
WHERE 
`uid`='".$_SESSION['uid']."' 
AND 
WEEKOFYEAR(`date`)=WEEKOFYEAR(NOW()) 
LIMIT 1

【问题讨论】:

查看这个答案:***.com/a/14294565 Find the ranking of an integer in mysql的可能重复 【参考方案1】:

我不能给你确切的代码,但我认为下面可以给你一些想法

从中选择“RANK-NR”、“USERNAME”、“VOTED” ( 选择 'RANK-NR', 'USERNAME', 'VOTED', rank() over (order by 'voted' desc) 作为排名 来自用户排名 在哪里 uid='".$_SESSION['uid']."' 和 星期几(date)=星期几(现在()) ) 作为 abc 在哪里 排名

我认为 rank() over (order by) 应该可以工作

【讨论】:

【参考方案2】:

我刚刚发现这个解决方案有效:

SELECT *
FROM 
(
  SELECT  @ranking:= @ranking + 1 rank,
          a.`uid`
  FROM    `users_ranking` a, (SELECT @ranking := 0) b
  ORDER BY a.`votes` DESC
) s
WHERE `uid`='".$_SESSION['uid']."' 
AND 
WEEKOFYEAR(`date`)=WEEKOFYEAR(NOW()) 
LIMIT 1

【讨论】:

严格来说,我认为您应该按顺序(从子查询中)从 users_ranking 中获取记录,然后将排名添加到其中。这将强制 MySQL 在计算排名之前按顺序获得投票。 你能发一个例子吗? @Kickstart【参考方案3】:

好的,以我的评论为例。您所拥有的通常会起作用,但没有什么可以强制 MySQL 在应用排名之前进行排序。

因此,使用额外级别的子查询会给你这个(未经测试)。内部子查询以正确的顺序获取相关周的所有用户 ID,而下一个外部子查询将排名应用于此有序结果集。外部查询只获取您需要的单个返回行。

SELECT c.rank, c.uid
FROM
(
    SELECT @ranking:= @ranking + 1 rank, a.uid
    FROM 
    (
        SELECT uid, votes
        FROM    `users_ranking`
        WHERE WEEKOFYEAR(`date`) = WEEKOFYEAR(NOW()) 
        ORDER BY votes DESC
    ) a,
    (SELECT @ranking := 0) b
) c
WHERE c.uid = '".$_SESSION['uid']."' 
LIMIT 1

另一种避免子查询并避免需要变量的可能性是进行连接。这是(错误)使用 HAVING 将结果缩小到您感兴趣的单行。此解决方案的缺点是,如果多个用户具有相同的分数,他们将各自获得相同的排名。

SELECT b.uid, COUNT(a.uid) 
FROM users_ranking a
LEFT OUTER JOIN users_ranking b
ON WEEKOFYEAR(a.`date`) = WEEKOFYEAR(b.`date`) 
AND a.votes >= b.votes
GROUP BY b.uid
HAVING b.uid = '".$_SESSION['uid']."' 

编辑

给出前10名的排名:-

SELECT b.uid, COUNT(a.uid) AS rank
FROM users_ranking a
LEFT OUTER JOIN users_ranking b
ON WEEKOFYEAR(a.`date`) = WEEKOFYEAR(b.`date`) 
AND a.votes >= b.votes
GROUP BY b.uid
ORDER BY rank
LIMIT 10

虽然在这种情况下使用子查询可能会更快。然后,您可以将 LIMIT 子句与 ORDER BY 一起放在子查询中,因此只需使用变量将排名添加到 10 行。

我不确定如何将其与单个用户的查询结合起来,主要是因为我不确定您想如何将两个结果合并在一起。

【讨论】:

感谢您的详细回复。但是,在我在这里将解决方案标记为正确之前,您能否再解释一下您的解决方案?这是关于性能问题吗?我的 MySQL 语句到底发生了什么不同?此外,不能有两个用户的投票数相同,因为我正在验证这在我的系统中是不允许的。 第一个解决方案在添加排名之前强制排序。这是对您自己的解决方案的简单升级。这个解决方案(和你的)的缺点是它使用子查询,这些往往会导致性能不佳(因为 MySQL 通常不会从子查询中携带索引),而且它会强制内部查询带回应用了排名的所有记录。我的第二个解决方案避免了子查询并且应该使用索引(使用 INNER JOIN 而不是 LEFT OUTER JOIN 可能更有效)。 我对子查询不熟悉,所以直到现在我才知道性能不佳。我现在在很多网站上都读到了。所以我想使用加入解决方案。但是存在您的第二个解决方案分配给特定用户的问题。在该 select 语句之上,我有所有用户的 Top10 列表。所以有两个查询。是否可以以某种方式将它们缩短为一个查询以获得 top10 列表以及这个特定的登录用户?这是我的代码:pastebin.com/xY00gG4g 为此添加了建议,但不确定是否将 2 个查询合并在一起。

以上是关于MySQL select 语句 - 如何计算当前排名的主要内容,如果未能解决你的问题,请参考以下文章

select语句中的mysql计算

mysql语句如何传递日期参数 - 技术问答

如何从当前 MySQL 数据库中查询和重新组织数据?

mysql09---sql语句优化

OracleSQL 中Select语句完整的执行顺序

MySQL不常用语句