【问题标题】:MySQL select statement - How to calculate the current rankingMySQL select 语句 - 如何计算当前排名
【发布时间】: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

【问题讨论】:

标签: php mysql select


【解决方案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 个查询合并在一起。
    猜你喜欢
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-26
    相关资源
    最近更新 更多