Assign a position value to row based on a SUM - mysql

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;

Related

combining two select statements with group by and order by

I have the following 2 select statements that work and return the info on their own, but was wondering if there was a way to join / combine them into one report with their own separate rows and columns
Select Sum (amount) as PDCGross
From Desk D Left Join
Master M
on D.code = M.desk INNER JOIN
pdc
ON m.number = pdc.number
Where teamid = 3
AND active = 1
And onhold is NULL
And deposit >='2020-10-01'
And deposit <= '2020-10-31'
Group by D.Name
Order by desk.Name Desc
Select Sum (amount) as GrossPDCC
FROM Desk D left JOIN
Master M
on D.code = M.Desk INNER JOIN
DebtorCreditCards P
ON m.number = P.Number
Where teamid = 3
AND IsActive = 1
And OnHoldDate is NULL
And DepositDate >='2020-10-01'
And DepositDate <= '2020-10-31'
Group by D.Name
Order by desk.Name Desc
The return for the 1st statement
PDCPross
2500
1500
1300
The result of the 2nd statement is
PDCCGross
1500
1300
1000
What i am looking for is
PDCPross PDCCGross
2500 1500
1500 1300
1300 1000
On MySQL v 8.0+ or MariaDB 10.2 +, you can use ROW_NUMBER() function:
Assign each query with ROW_NUMBER() then make both query as sub-query. Join the queries using the rownum result and you should be able to see your results side by side. Something like this query example:
SELECT A.PDCGross, B.GrossPDCC FROM
(SELECT ROW_NUMBER() OVER (ORDER BY D.Name DESC) AS rownum, SUM(amount) AS PDCGross
FROM Desk D LEFT JOIN
MASTER M
ON D.code = M.desk INNER JOIN
pdc
ON m.number = pdc.number
WHERE teamid = 3
AND active = 1
AND onhold IS NULL
AND deposit >='2020-10-01'
AND deposit <= '2020-10-31'
GROUP BY D.Name
ORDER BY D.Name DESC) A JOIN
(SELECT ROW_NUMBER() OVER (ORDER BY D.Name DESC) AS rownum, SUM(amount) AS GrossPDCC
FROM Desk D LEFT JOIN
MASTER M
ON D.code = M.Desk INNER JOIN
DebtorCreditCards P
ON m.number = P.Number
WHERE teamid = 3
AND IsActive = 1
AND OnHoldDate IS NULL
AND DepositDate >='2020-10-01'
AND DepositDate <= '2020-10-31'
GROUP BY D.Name
ORDER BY D.Name DESC) B ON A.rownum=B.rownum;

SQL Rank Function with Group

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

Get ranking on mysql produce wrong ranks

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;

Merge 2 SQL Queries/Tables

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;

Selecting last 5 positions of all users from a join

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