Reputation: 45
I got help with this SQL-question below to sum up top 5 lenght for each team. The problem for me now seems that SQL is randomly selecting 5 of the for eg 7 rows in the database for team 25, not the top 5. Anyone has any ideas for me? Would be much appriciated!
select team, sum(length) as totalScore
from
(
SELECT
t.*,
@num_in_group:=case when @team!=team then @num_in_group:=0 else @num_in_group:=@num_in_group+1 end as num_in_group,
@team:=team as t
FROM reg_catches t, (select @team:=-1, @num_in_group:=0) init
ORDER BY team asc
) sub
WHERE
sub.num_in_group <= 4 and
competition = 16 and
team = 25
GROUP BY team
ORDER BY totalScore DESC;
Table
team length competition
----------------------
26 70 16
25 70 16
25 95 16
25 98 16
25 100 16
25 100 16
25 100 16
25 122 16
Wanted output:
team totalScore
---- -----------
25 520
26 70
Best regards, Christian
Upvotes: 3
Views: 792
Reputation: 1447
If you wanted to get the top 5 rows for each team
, you could utilise a parameter to rank each team's results:
SELECT *,
(@rank := if(@team = team, @rank + 1, if(@team := team, 1, 1))) as rank
FROM (SELECT * from reg_catches order by team, length desc) ordered
HAVING rank <= 5
ORDER BY team, rank asc
Which would produce the following: http://sqlfiddle.com/#!9/6042ae/1
| team | length | competition | rank |
+------+--------+-------------+------+
| 25 | 99 | 16 | 1 |
| 25 | 98 | 16 | 2 |
| 25 | 77 | 16 | 3 |
| 25 | 76 | 16 | 4 |
| 25 | 73 | 16 | 5 |
| 26 | 96 | 16 | 1 |
| 26 | 88 | 16 | 2 |
| 26 | 87 | 16 | 3 |
| 26 | 83 | 16 | 4 |
| 26 | 79 | 16 | 5 |
You could then, if required, wrap that query in a simple aggregation/group to get the total of the top 5 for each team
SELECT team, sum(length) as 'total score' FROM
(SELECT *,
(@rank := if(@team = team, @rank + 1, if(@team := team, 1, 1))) as rank
FROM (SELECT * from reg_catches order by team, length desc) ordered
HAVING rank <= 5
ORDER BY team, rank asc) top5
GROUP BY team
As per http://sqlfiddle.com/#!9/6042ae/2
| team |total score|
+-------+-----------+
| 25 | 423 |
| 26 | 433 |
| 27 | 426 |
The usual caveats will apply to parameters, but you can't really 'rank' in MySQL without them. I also found this very sensitive to initially getting the scores in the right order before applying the parameters.
Whilst this might not be the most polished solution, I hope it helps
Upvotes: 1
Reputation: 521093
Besides any logical problems you have with your current query, there may be other risks in the form of the behavior of MySQL session variables. MySQL does not guarantee the order in which assignments to session variables will take place in a select statement. Hence, a query to simulate rank with a partition (your problem) may not be reliable. But we can still solve the problem by using a correlated subquery to label the top 5 lengths for each team:
SELECT
team,
SUM(length) AS totalScore
FROM
(
SELECT
team,
length,
competition,
(SELECT COUNT(*) FROM reg_catches t2
WHERE t2.team = t1.team AND t2.length >= t1.length) cnt
FROM reg_catches t1
) t
WHERE
cnt <= 5 AND
competition = 16
GROUP BY
team
ORDER BY
team;
Note that this query has a limitation should there be ties present. If you expect ties then we could work around this by using some other column to break the tie.
Output:
team totalScore
1 25 520
2 26 70
Demo here:
Upvotes: 1
Reputation: 64466
In your inner query add another ordering clause as
ORDER BY team asc ,length desc
This will order rows of table first with team and then for each team it will order rows by length column value, so higher value for team will be returned first
Or another way to get you the same results without using variables
select a.team, sum(a.length) as totalScore
from (
select b.*,(
select count(*)
from reg_catches c
where b.team = c.team
and b.length < c.length
) + 1 rownum
from reg_catches b
) a
where a.rownum <=5
group by a.team
order by totalScore DESC;
Make sure to have an index on team and length column to get better performance
Upvotes: 1