繁体   English   中英

仅当2列(持续时间和得分)不同时,才如何在mysql中增加排名?

[英]How to increment rank in mysql only when the 2 columns (duration and score) are different?

细节

我结合了以下表格

检测结果

--------------------------------------------------------------------
| index | uid|         start         |          stop        | score| 
--------------------------------------------------------------------
|   1   | 23 |   2012-06-06 07:30:20 | 2012-06-06 07:30:34  | 100  |
--------------------------------------------------------------------
|   2   | 34 |   2012-06-06 07:30:21 | 2012-06-06 07:30:40  | 100  |
--------------------------------------------------------------------

用户表

------------------------------
| id  |       username       |  
------------------------------
| 23  |    MacGyver’s mum    | 
------------------------------
| 34  |       Gribblet       | 
------------------------------

使用这个SQL

SELECT a.username, b.duration, b.score
FROM usertable AS a
JOIN    (SELECT `uid`, `score`,
TIMESTAMPDIFF( SECOND, start, stop ) AS `duration`
FROM `testresults`
WHERE `start` >= DATE(NOW())
ORDER BY `score` DESC, `duration` ASC
LIMIT 100) AS b
ON a.id = b.uid

问题是I want to rank the results 我认为在sql中而不是在php中可能更容易/更快,因此基于http://code.openark.org/blog/mysql/sql-ranking-without-self-join,这是我尝试过的

SELECT a.username, b.duration, b.score, COUNT(DISTINCT b.duration, b.score) AS rank
FROM usertable AS a
JOIN    (SELECT `uid`, `score`,
TIMESTAMPDIFF( SECOND, start, stop ) AS `duration`
FROM `testresults`
WHERE `start` >= DATE(NOW())
ORDER BY `score` DESC, `duration` ASC
LIMIT 100) AS b
ON a.id = b.uid

但我没有得到预期的排名。 它仅返回一行。

我究竟做错了什么? 仅当持续时间和分数都唯一时,如何才能提高排名?

UPDATE1

使用bdenham的“慢速方法”对我有用,但是第二种方法没有效果。 我不太了解“快速方法”中发生了什么。 我已经发布了我正在使用的数据和结果表。 您会看到排名混乱。

 -------------------------------------------------------------------
| index | uid|         start         |          stop        | score| 
--------------------------------------------------------------------
|   1   | 32 |  2012-08-27 05:47:18  |  2012-08-27 05:47:36 |  100 | 18s
|   2   | 32 |  2012-08-27 05:50:36  |  2012-08-27 05:50:42 |   0  |  6s
|   3   | 32 |  2012-08-27 05:51:18  |  2012-08-27 05:51:25 |  100 |  7s
|   4   | 32 |  2012-08-27 05:51:30  |  2012-08-27 05:51:35 |   0  |  5s
|   5   | 32 |  2012-08-27 05:51:39  |  2012-08-27 05:51:44 |   50 |  5s
--------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| username | score | duration | @prevScore:=@currScore | @prevDuration:=@currDuration | @currScore:=r.score | @currDuration:=timestampdiff(second,r.start,r.stop) |rank |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   bob    |  100  |    7     |     [BLOB - 1B]        |         [BLOB - 1B]          |     100             |                                7                    |  3  |
|   bob    |  100  |    18    |     [BLOB - 0B]        |         [BLOB - 0B]          |     100             |                               18                    |  1  |
|   bob    |   50  |    5     |     [BLOB - 1B]        |         [BLOB - 1B]          |      50             |                                5                    |  5  |
|   bob    |   0   |    5     |     [BLOB - 3B]        |         [BLOB - 1B]          |       0             |                                5                    |  4  |
|   bob    |   0   |    6     |     [BLOB - 3B]        |         [BLOB - 2B]          |       0             |                                6                    |  2  |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

问题链接中的两种方法均适用于MySQL 5.5.25。 这是SQL Fiddle 但是我无法将这些方法适应于您稍微复杂一些的模型。 您还有一个额外的联接,加上您的排名基于两列而不是仅一列。

尽管我怀疑您正在尝试遵循缓慢的“传统”解决方案,但是您的尝试并未遵循这两种方法。 正如其他人指出的那样,该解决方案需要您完全缺乏的自我加入和分组。

这是我尝试使慢速方法适应模型的重大尝试。 问题是MySQL只保留为给定排名找到的最后一行的用户名。 具有相同等级的较早的行将从结果中丢弃。 该查询将不会在大多数数据库上运行,因为GROUP BY不包括用户名。 MySQL对于GROUP BY具有非标准规则。 我不明白为什么您的中等复杂的模型不起作用,但是简单的链接模型却起作用。 我认为无论如何都缺少GROUP BY条款是一个坏主意。

select u.username,
       r1.score,
       timestampdiff(second,r1.start,r1.stop) duration,
       count( distinct concat(r2.score,',',timestampdiff(second,r2.start,r2.stop)) ) rank
  from testresults r1
  join testresults r2
    on r2.score>r1.score
     or( r2.score=r1.score
         and
         timestampdiff(second,r2.start,r2.stop)<=timestampdiff(second,r1.start,r1.stop)
       )
  join usertable u
    on u.id=r1.uid
 where r1.start>=date(now())
   and r2.start>=date(now())
 group by r1.score, duration
 order by score desc, duration asc limit 100

这是慢速方法的解决方法。 它首先计算每个唯一得分/持续时间对的等级,然后将该结果与每个测试结果相加。 这行得通,但比原始的坏方法还要慢。

select username,
       r.score,
       r.duration,
       r.rank
  from testresults tr
  join usertable u
    on u.id=tr.uid
  join (
          select r1.score,
                 timestampdiff(second,r1.start,r1.stop) duration,
                 count( distinct concat(r2.score,',',timestampdiff(second,r2.start,r2.stop)) ) rank
            from testresults r1
            join testresults r2
              on r2.score>r1.score
               or( r2.score=r1.score
                   and
                   timestampdiff(second,r2.start,r2.stop)<=timestampdiff(second,r1.start,r1.stop)
                 )
           where r1.start>=date(now())
             and r2.start>=date(now())
           group by r1.score, duration
       ) r
    on r.score=tr.score
   and r.duration=timestampdiff(second,tr.start,tr.stop)
 where tr.start>=date(now())
 order by rank limit 100

这是我尝试使快速方法适合您的模型的一次失败尝试。 该方法不起作用,因为所选变量是在排序操作之前计算的。 再次说明,我不理解为什么链接中的简单模型有效,但您的模型无效。

select u.username,
       r.score,
       timestampdiff(second,r.start,r.stop) duration,
       @prevScore:=@currScore,
       @prevDuration:=@currDuration,
       @currScore:=r.score,
       @currDuration:=timestampdiff(second,r.start,r.stop),
       @rank:=if(@prevScore=@currScore and @prevDuration=@currDuration, @rank, @rank+1) rank
  from testresults r
  join usertable u
    on u.id=r.uid
  cross join (select @currScore:=null, @currDuration:=null, @prevScore:=null, @prevDuration:=null, @rank:=0) init
 where r.start>=date(now())
 order by score desc, duration asc limit 100

这是快速方法的“固定”版本。 但是它依赖于子查询中已排序行的顺序。 通常,除非存在显式SORT操作,否则查询永远不应依赖行的顺序。 外部查询没有排序,即使是,我也不知道变量是在外部排序之前还是之后进行计算。

select username,
       score,
       duration,
       @prevScore:=@currScore,
       @prevDuration:=@currDuration,
       @currScoure:=score,
       @currDuration:=duration,
       @rank:=if(@prevScore=score and @prevDuration=duration, @rank, @rank+1) rank
  from (
          select u.username,
                 r.score,
                 timestampdiff(second,r.start,r.stop) duration
            from testresults r
            join usertable u
              on u.id=r.uid
           where r.start>=date(now())
           order by score desc, duration asc limit 100
       ) scores,
       (
          select @currScore:=null, 
                 @currDuration:=null, 
                 @rank:=0
       ) init

我认为,如果您仅选择没有排名的结果(按分数和持续时间排序),您将获得同样好的性能。 由于结果已经排序,因此您的PHP可以有效地计算排名。 您的PHP可以将等级初始化为0,将上一个分数和持续时间初始化为null。 然后将每行与先前的值进行比较,如果存在差异,则增加等级。 让PHP对排序后的结果进行排名的最大好处是,无论数据库引擎的品牌或版本如何,PHP始终可以正常工作。 而且它应该仍然很快。

这是显示所有4条查询的SQL Fiddle 我修改了WHERE子句,以便查询可以在任何日期继续工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM