简体   繁体   English

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

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

DETAILS 细节

I've combined the following tables 我结合了以下表格

testresults 检测结果

--------------------------------------------------------------------
| 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  |
--------------------------------------------------------------------

usertable 用户表

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

using this sql 使用这个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

Problem is I want to rank the results . 问题是I want to rank the results I think it is probably easier/faster to do it in sql as opposed to php, so based on http://code.openark.org/blog/mysql/sql-ranking-without-self-join this is what I tried 我认为在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

but I don't get back the expected ranks. 但我没有得到预期的排名。 It only returns one row. 它仅返回一行。

QUESTION

What am I doing wrong? 我究竟做错了什么? How can I increase rank only when duration and score are unique? 仅当持续时间和分数都唯一时,如何才能提高排名?

UPDATE1 UPDATE1

Using bdenham's "slow method" worked for me, but the second method didn't. 使用bdenham的“慢速方法”对我有用,但是第二种方法没有效果。 I don't really understand what is going on in the "fast method". 我不太了解“快速方法”中发生了什么。 I've posted the data I was using and the resulting table. 我已经发布了我正在使用的数据和结果表。 You'll see that the ranking is messed up. 您会看到排名混乱。

 -------------------------------------------------------------------
| 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  |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Both methods from the link in your question work with MySQL 5.5.25. 问题链接中的两种方法均适用于MySQL 5.5.25。 Here is the SQL Fiddle . 这是SQL Fiddle But I am not able to adapt the methods to your slightly more complicated model. 但是我无法将这些方法适应于您稍微复杂一些的模型。 You have an additional join, plus your rank is based on two columns instead of just one. 您还有一个额外的联接,加上您的排名基于两列而不是仅一列。

Your attempt does not follow either method, though I suspect you were attempting to follow the slow "traditional" solution. 尽管我怀疑您正在尝试遵循缓慢的“传统”解决方案,但是您的尝试并未遵循这两种方法。 As others have pointed out, that solution requires a self join and group by that you are completely lacking. 正如其他人指出的那样,该解决方案需要您完全缺乏的自我加入和分组。

Here is my broken attempt at adapting the slow method to your model. 这是我尝试使慢速方法适应模型的重大尝试。 The problem is MySQL only preserves the username of the last row found for a given rank. 问题是MySQL只保留为给定排名找到的最后一行的用户名。 Earlier rows with the same rank are discarded from the results. 具有相同等级的较早的行将从结果中丢弃。 The query would not run on most databases because the GROUP BY does not include username. 该查询将不会在大多数数据库上运行,因为GROUP BY不包括用户名。 MySQL has non-standard rules for GROUP BY. MySQL对于GROUP BY具有非标准规则。 I don't understand why your moderately complicated model doesn't work, but the simple linked model does work. 我不明白为什么您的中等复杂的模型不起作用,但是简单的链接模型却起作用。 I think it is a bad idea to have missing GROUP BY terms anyway. 我认为无论如何都缺少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

Here is a fix for the slow method. 这是慢速方法的解决方法。 It first computes the rank for each unique score/duration pair, then joins that result with each test result. 它首先计算每个唯一得分/持续时间对的等级,然后将该结果与每个测试结果相加。 This works, but it is even slower than the original broken method. 这行得通,但比原始的坏方法还要慢。

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

Here is my broken attempt at adapting the fast method to your model. 这是我尝试使快速方法适合您的模型的一次失败尝试。 The method does not work because the selected variables are computed prior to the sort operation. 该方法不起作用,因为所选变量是在排序操作之前计算的。 Again I don't understand why the simple model in the link works but your model does not. 再次说明,我不理解为什么链接中的简单模型有效,但您的模型无效。

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

Here is a "fixed" version of the fast method. 这是快速方法的“固定”版本。 But it relies on the order of the sorted rows in the subquery. 但是它依赖于子查询中已排序行的顺序。 In general a query should never rely on the order of rows unless there is an explicit SORT operation. 通常,除非存在显式SORT操作,否则查询永远不应依赖行的顺序。 The outer query is not sorted, and even if it were, I don't know if the variables would be computed before or after the outer 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

I think you will get just as good performance if you simply select the results without rank, ordered by score and duration. 我认为,如果您仅选择没有排名的结果(按分数和持续时间排序),您将获得同样好的性能。 Your PHP can efficiently compute the rank since the results are already sorted. 由于结果已经排序,因此您的PHP可以有效地计算排名。 Your PHP can initialize rank to 0 and prev score and duration to null. 您的PHP可以将等级初始化为0,将上一个分数和持续时间初始化为null。 Then compare each row with the previous values and increment the rank if there is a difference. 然后将每行与先前的值进行比较,如果存在差异,则增加等级。 The big advantage of letting PHP rank the sorted results is it should always work, regardless of the brand or version of the database engine. 让PHP对排序后的结果进行排名的最大好处是,无论数据库引擎的品牌或版本如何,PHP始终可以正常工作。 And it should still be fast. 而且它应该仍然很快。

Here is the SQL Fiddle showing all 4 queries. 这是显示所有4条查询的SQL Fiddle I modified the WHERE clauses so that the queries continue to work for any date. 我修改了WHERE子句,以便查询可以在任何日期继续工作。

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

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