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
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 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
I spent so much time googling today but i don't even know which keywords to use. So …
The project is an evaluation of a betting game (Football). I have 2 SQL Queries:
SELECT players.username, players.userid, matchdays.userid, matchdays.points, SUM(points) AS gesamt
FROM players INNER JOIN matchdays ON players.userid = matchdays.userid AND matchdays.season_id=5
GROUP BY players.username
ORDER BY gesamt DESC
And my second query:
SELECT max(matchday) as lastmd, points, players.username from players INNER JOIN matchdays ON players.userid = matchdays.userid WHERE matchdays.season_id=5 AND matchday=
(select max(matchday) from matchdays)group by players.username ORDER BY points DESC
The first one adds up the points of every matchday and shows the sum.
The second shows the points of the last gameday.
My Goal is to merge those 2 queries/tables so that the output is a table like
Rank | Username | Points last gameday | Overall points |
I don't even know where to start or what to look for. Any help would be appreciated ;)
use both query with join....use inner join if each userid have value in 2nd query also.also add userid in 2nd query also for join
SET #rank = 0;
SELECT #rank := rank + 1,
t1.username,
t2.points,
t1.gesamt
FROM (
SELECT players.username, players.userid puserid, matchdays.userid muserid, matchdays.points, SUM(points) AS gesamt
FROM players INNER JOIN matchdays ON players.userid = matchdays.userid AND matchdays.season_id=5
GROUP BY players.username
)t1
INNER JOIN
(
SELECT players.userid, max(matchday) as lastmd, points, players.username
from players INNER JOIN matchdays ON players.userid = matchdays.userid
WHERE matchdays.season_id=5 AND matchday=
(select max(matchday) from matchdays)group by players.username
)t2
ON t1.puserid = t2.userid
ORDER BY t1.gesamt
You can use conditional aggregation, i.e. sum the points only when the day is the last day:
SELECT
p.username,
SUM(case when m.matchday = (select max(matchday) from matchdays) then m.points end)
AS last_day_points,
SUM(m.points) AS total_points
FROM players p
INNER JOIN matchdays m ON p.userid = m.userid AND m.season_id = 5
GROUP BY p.userid
ORDER BY total_points DESC;
Or with a join instead of a non-correlated subquery (MySQL should come to the same execution plan):
SELECT
p.username,
SUM(case when m.matchday = last_day.matchday then m.points end) AS last_day_points,
SUM(m.points) AS total_points
FROM players p
INNER JOIN matchdays m ON p.userid = m.userid AND m.season_id = 5
CROSS JOIN
(
select max(matchday) as matchday
from matchdays
) last_day
GROUP BY p.userid
ORDER BY total_points DESC;
PlayerLeague
league_id
player_id
player_position
PlayerStat
player_id
score
I have two tables, and I want to update PlayerLeague, set player_position equal row number in:
SELECT * FROM PlayerStat s JOIN PlayerLeague l
ON s.player_id=l.player_id WHERE l.league_id=3 ORDER BY score DESC;
I tried to use #i in my select, but with join ORDER BY was ignored.
UPDATE
So, now I have this and it's working, but I'm not sure that it's normal way.
It will be run every hour for three leagues 40-500 players.
SET #i=0;
UPDATE PlayerLeague ll set ll.player_position = ( SELECT position FROM (SELECT
s.player_id, #i:=#i+1 as position
FROM PlayerStat s
where s.player_id in
(SELECT player_id from PlayerLeague l WHERE l.league_id = 3)
ORDER BY score DESC) AS t WHERE t.player_id=ll.player_id);
Meet this little cute monster:
UPDATE PlayerLeague AS l
JOIN (
SELECT p.player_id, #i := #i + 1 AS player_position
FROM (
SELECT ss.player_id
FROM PlayerStat AS ss
JOIN PlayerLeague AS ll
ON ss.player_id=ll.player_id
WHERE ll.league_id=3
ORDER BY ss.score DESC
) AS p, (SELECT #i := 0) AS tmp
) AS s ON s.player_id=l.player_id
SET l.player_position = s.player_position
So, what's happening here?
Innermost query selects all players in a league in order of their scores.
It is wrapped in a query that adds row counts.
Finally, the outer UPDATE does what you need: adds row counts into PlayerLeague table.
try
SELECT *
FROM PlayerStat s
where s.player_id in
(select player_id from PlayerLeague l WHERE l.league_id = 3)
ORDER BY score DESC;
I'm using this query:
SELECT A.place_idx,A.place_id,B.TOTAL_CNT,(#r := #r + 1) AS rank FROM CUSTOM_LIST
AS A
INNER JOIN
(SELECT #r := 0)
AS C
INNER JOIN
(SELECT place_id,COUNT(place_id) AS TOTAL_CNT from COUNT_TABLE GROUP BY place_id)
AS B ON B.place_id=A.place_id order by B.TOTAL_CNT desc;
Which gives this result:
But I want this result:
How do I need to modify my query? What am I doing wrong?
SELECT *,(#r := #r + 1) AS rank FROM
(
SELECT A.place_idx,A.place_id,B.TOTAL_CNT FROM CUSTOM_LIST
AS A
INNER JOIN
(SELECT place_id,COUNT(place_id) AS TOTAL_CNT from COUNT_TABLE GROUP BY place_id)
AS B ON B.place_id=A.place_id order by B.TOTAL_CNT desc
) AS T, (SELECT #r := 0) AS tt
Your C.rank is getting calculated as they are processed, not after they are sorted. There is really no need for this data, anyways. Since you're sorting the rows by your metric, you know the first row is the first rank, etc. You can handle it on the programming side of things after you pull it out.
Alternatively, you can put what you have in an inner select, then do the rank after.