[英]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.