I want to rank the total stats of a group of users and assign a rank variable to them.
I used this thread for the Rank variable.
This is my Query atm:
SELECT #rank := #rank + 1 AS rank
, SUM(stats.points) AS x
FROM
( SELECT #rank := 0 ) r
, groups
LEFT
JOIN user_group
ON groups.id = user_groups.clan
LEFT
JOIN stats
ON user_groups.user = stats.id
GROUP
BY groups.id
ORDER
BY x DESC
RANK | points
--------------
47 | 3400
1 | 2500
75 | 1200
As you can see the Sorting by Points works fine, but the Rank variable seems to just pick random values.
Can anyone find a way to assign the rank correctly?
Use a subquery for the aggregation and ordering:
SELECT id, sum_points, #rank := #rank + 1 AS rank
FROM (SELECT g.id, SUM(s.points) AS sum_points
FROM groups g LEFT JOIN
user_group ug
ON g.id = ug.clan LEFT JOIN
stats s
ON ug.user = s.id
GROUP BY g.id
ORDER BY sum_points DESC
) s CROSS JOIN
(SELECT #rank := 0) params;
This has been an issue in MySQL for a while -- variables don't work well with aggregation and ordering.
Note that in MySQL 8+, this is much more simply written as:
SELECT g.id, SUM(s.points) AS sum_points,
ROW_NUMBER() OVER (ORDER BY SUM(s.points) DESC) as rank
FROM groups g LEFT JOIN
user_group ug
ON g.id = ug.clan LEFT JOIN
stats s
ON ug.user = s.id
GROUP BY g.id
Related
I'm using the code below to get the total distance for teams that people have registered to and entered data, then assign a position to the team.
SELECT #curRow := #curRow + 1 AS position, ROUND(SUM(d.dist_activity_duration
* CASE
WHEN d.dist_is_distance = 0 THEN s.activity_steps / 2000
WHEN d.dist_is_distance = 1 THEN 1
END)
,2) AS miles, t.team_name AS team_name
FROM distance d
JOIN (SELECT #curRow := 0) r
JOIN activities a
ON a.id = d.dist_activity_id
JOIN steps s
ON s.id = a.steps_id
JOIN members AS m
ON d.member_id = m.id
JOIN teams AS t
ON t.id = m.member_team_id
GROUP BY team_name
ORDER BY miles DESC
The code above outputs the following results
position miles team_name
2 134.05 team 1
1 78.00 team 2
I would like position 1 to be assigned to the team with the highest miles, position 2 the 2nd highest team...and so on.
In MySQL 8+, you would just use row_number():
SELECT ROW_NUMBER() OVER (ORDER BY miles DESC) AS position, t.*
FROM (SELECT ROUND(SUM(d.dist_activity_duration *
CASE WHEN d.dist_is_distance = 0 THEN s.activity_steps / 2000
WHEN d.dist_is_distance = 1 THEN 1
END), 2) AS miles, t.team_name AS team_name
FROM distance d JOIN
activities a
ON a.id = d.dist_activity_id JOIN
steps s
ON s.id = a.steps_id JOIN
members m
ON d.member_id = m.id JOIN
teams t
ON t.id = m.member_team_id
GROUP BY team_name
) t
ORDER BY miles DESC;
Earlier versions of MySQL support variables but they do not play well with GROUP BY and ORDER BY. The solution is a subquery (as above):
SELECT (#rn := #rn + 1) AS position,
FROM (SELECT ROUND(SUM(d.dist_activity_duration *
CASE WHEN d.dist_is_distance = 0 THEN s.activity_steps / 2000
WHEN d.dist_is_distance = 1 THEN 1
END), 2) AS miles, t.team_name AS team_name
FROM distance d JOIN
activities a
ON a.id = d.dist_activity_id JOIN
steps s
ON s.id = a.steps_id JOIN
members m
ON d.member_id = m.id JOIN
teams t
ON t.id = m.member_team_id
GROUP BY team_name
ORDER BY miles DESC
) t CROSS JOIN
(SELECT #rn := 0) params;
This works for me.
SELECT (#rn := #rn + 1) AS position, team_name, miles
FROM (SELECT ROUND(SUM(d.dist_activity_duration
* CASE
WHEN d.dist_is_distance = 0 THEN s.activity_steps / 2000
WHEN d.dist_is_distance = 1 THEN 1
END)
,2) AS miles, t.team_name AS team_name
FROM distance d
JOIN activities a
ON a.id = d.dist_activity_id
JOIN steps s
ON s.id = a.steps_id
JOIN members AS m
ON d.member_id = m.id
JOIN teams AS t
ON t.id = m.member_team_id
GROUP BY team_name
ORDER BY miles DESC
) t CROSS JOIN
(SELECT #rn := 0) params;
i'm trying to get ranking based on rating percentage so mysql query like
select c.id , sum((r.value * 20))/ count(r1.pagetypeid) as score, #curRank := #curRank + 1 AS rank from (SELECT #curRank := 0) cr, rating as r
inner join rateelement as r1 on r.elementid = r1.id
inner join ratesubscription as r2 on r.subscriptionid = r2.id
inner join consultant as c on r2.consultantid = c.id
where r1.displayorder not in (6) and r2.agencyid = 38
group by c.id order by score desc
but it returns wrong raking indexes
what's wrong with the query?
Ranking with variables often has issues with group by -- and even order by in the most recent versions of MySQL. So, use a subquery:
select x.*, (#curRank := #curRank + 1) AS rank
from (select c.id, sum((r.value * 20))/ count(r1.pagetypeid) as score
from rating r inner join
rateelement r1
on r.elementid = r1.id inner join
ratesubscription r2
on r.subscriptionid = r2.id inner join
consultant c
on r2.consultantid = c.id
where r1.displayorder not in (6) and r2.agencyid = 38
group by c.id
order by score desc
) x cross join
(SELECT #curRank := 0) cr;
My Query:
SET #rank = 0;
SELECT Jobs.ID, Jobs.StatusID, (#rank:=#rank+1) AS Rank
FROM Jobs
INNER JOIN JobStatuses ON Jobs.StatusID = JSID
INNER JOIN JobStatusGroups ON Jobs.SGrID = JSGID
WHERE Jobs.StatusID = 3
ORDER BY JobTitle DESC
LIMIT 5
Results in:
1010 3 1
1011 3 2
1013 3 4
1014 3 5
1016 3 7
Should result in:
1010 3 1
1011 3 2
1013 3 3
1014 3 4
1016 3 5
How can I make #rank only increment if row is actually being inserted? I tried cross join did not work. I also tried the following:
SELECT Jobs.ID, Jobs.StatusID, (#rank:=#rank+1) AS Rank
FROM Jobs, (SELECT #rank := 0) tempRank
INNER JOIN JobStatuses ON Jobs.StatusID = JSID
INNER JOIN JobStatusGroups ON Jobs.SGrID = JSGID
WHERE Jobs.StatusID = 3
ORDER BY JobTitle DESC
LIMIT 5
But gives me an error saying Jobs.StatusID does not exist.
instead of setting it outside cross join it in the select (i prefer cross join just so its easier to read aka i know where im creating the variable) that way the count wont be messed up
INSERT IGNORE INTO MyTable(MyID, UserID, JobID, StatusID, SortOrder)
SELECT :myid, :userid, Jobs.ID, Jobs.StatusID, (#rank:=#rank+1) AS Rank
FROM Jobs
INNER JOIN JobStatuses ON Jobs.StatusID = JSID
CROSS JOIN (SELECT #rank := 0) temp
LEFT JOIN JobStatusGroups ON Jobs.SGrID = JSGID
WHERE ...
ORDER BY JobTitle DESC
on terms of performance I haven't ever noticed performance issues with using the variables instantiation inside a join on the table
one thing you can try is maybe the ORDER BY is messing up the order / count so you can try encapsulating it in a subselect
SELECT ID, StatusID, (#rank:=#rank+1) AS Rank
FROM
( SELECT Jobs.ID, Jobs.StatusID
FROM Jobs
INNER JOIN JobStatuses ON Jobs.StatusID = JSID
INNER JOIN JobStatusGroups ON Jobs.SGrID = JSGID
WHERE Jobs.StatusID = 3
ORDER BY JobTitle DESC
LIMIT 5
)t
CROSS JOIN (SELECT #rank := 0) temp
Try This updated one:
SET #rank = 0;
INSERT INTO MyTable(MyID, UserID, JobID, StatusID, SortOrder)
SELECT myid, userid, Jobs.ID, Jobs.StatusID, (#rank:=#rank+1) AS Rank
FROM Jobs
INNER JOIN JobStatuses ON Jobs.StatusID = JobStatuses.JSID
LEFT JOIN JobStatusGroups ON Jobs.SGrID = JobStatusGroups.JSGID
ON DUPLICATE KEY update MyID=IF((#rank:=#rank-1) <> NULL IS NULL, VALUES(MyID), NULL);
I have 2 tables: player_positions (records the gps coordinates of all players) and players (records data for each player).
players
player_id, player_name
player_positions
player_id, player_lat, player_lon, timestamp
I want to get last 5 positions for all players.
My first query for selecting the last locations of one player is:
SELECT * FROM player_positions WHERE player_id = 1 ORDER BY timestamp DESC LIMIT 5
But I don't know how to duplicate this query to extract all players data. I believe I have to do a select within a previous join but not sure how to write it.
Thank you.
You can emulate the rownumber function found in other dbs like so, to solve this problem.
SELECT
player_id,
player_lat,
player_lon,
`timestamp`
FROM
(SELECT
pp.player_id,
pp.player_lat,
pp.player_lon,
pp.`timestamp`,
#rn := if(#prev = pp.player_id, #rn + 1,1) as rn,
#prev:=pp.player_id
FROM
Player_Positions as pp
JOIN (SELECT #Prev:= Null, #Rn := 0) as v
ORDER BY
pp.player_id,
pp.timestamp desc) as t
WHERE
rn <= 5;
Note that if for some reason there's a tie for 5th the database will arbitrarily pick one. Since it's unlikely that a player can be in two positions for the same timestamp you should be okay.
Try this :
SELECT pp.* , p.player_name
FROM player_positions pp
INNER JOIN players p ON p.player_id = pp.player_id
GROUP BY pp.player_id
ORDER BY pp.timestamp DESC LIMIT 5
EDIT:
SELECT pp.* , p.player_name
FROM player_positions pp
INNER JOIN players p ON p.player_id = pp.player_id
LEFT OUTER JOIN player_positions pp2 ON pp.player_id = pp2.player_id AND pp.timestamp < pp2.timestamp
GROUP BY pp.player_id
HAVING COUNT(*) < 5
ORDER BY pp.timestamp DESC LIMIT 5
I'm trying to follow this example: MySQL - ranking by count() and GROUP BY but my rank column keeps returning nil.
Here's my query:
SELECT
#rownum := #rownum+1 AS rank,
q.id,
q.Name,
q.count
FROM
(SELECT
Accounts.id,
Accounts.Name,
COUNT(Accounts.Name) AS count
FROM
player_to_team_histories
INNER JOIN
team_histories ON team_histories.id = player_to_team_histories.team_history_id
INNER JOIN
teams ON teams.id = team_histories.team_id
INNER JOIN
accounts ON accounts.id = teams.account_id
WHERE
accounts.AccountTypeId = 1 AND player_id IN (SELECT
player_id
FROM
player_to_team_histories
WHERE
player_to_team_histories.not_valid IS NULL AND team_history_id = (SELECT
team_history_id
FROM
player_to_team_histories
INNER JOIN
team_histories ON team_histories.id = player_to_team_histories.team_history_id
WHERE
player_to_team_histories.id = 574651))
GROUP BY Accounts.Name
ORDER BY count DESC)q
Every column except rank is returning as expected, and rank is returning null for every row.
You have to initialize your rownum to 0 before starting to increment it, either by
SET #rownum := 0;
before the query, or by a separate SELECT clause after your first FROM:
SELECT ...
FROM
(SELECT #rownum := 0 ) counter,
(SELECT
... )