mysql select number of row - mysql

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;

Related

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

MySQL: where count is higher than average

I want to select posts from users who have specific followers which is higher than the overall average (compared to other users)
The problem is when I use AVG() it limits the number of posts/users coming through, yet I can't use GROUP BY j.id as it will break the average count and WHERE j2.fCount >= j2.oAvg stops working properly
Here's my code
SELECT * FROM (
SELECT j.*, ROUND(AVG(j.fCount)) as oAvg
FROM (
SELECT p.id , COUNT(fCount.id) as fCount
FROM `post` p
LEFT JOIN `table` table ON ...
LEFT JOIN `user` user ON ....
LEFT JOIN `follow` fCount ON fCount.user_id=user.id AND fCount.follow_id=table.ids
WHERE p.user_id=fCount.user_id
group by p.id
) j
---- > `GROUP BY j.id` - BREAKS THE AVERAGE BELOW
) j2
WHERE j2.fCount >= j2.oAvg
Thank you :)
because you're trying to compare to average, you might have to do your inner query twice like this.
SELECT *,
(SELECT AVG(fCount) as average FROM
(SELECT COUNT(fCount.id) as fCount
FROM post p
LEFT JOIN follow fCount ON fCount.user_id = p.user_id
GROUP BY p.id
)j1
)as average
FROM
(SELECT p2.id, COUNT(fCount2.id) as fCount
FROM post p2
LEFT JOIN follow fCount2 ON fCount2.user_id = p2.user_id
GROUP BY p2.id
)j2
HAVING fCount >= average
sqlfiddle
just replace inner queries of j1 and j2 with your j
if you just want to run inner query once you can use user-defined variables to total up your count divide it by count to calculate your own average like this
SELECT id,fCount,#sum/#count as average
FROM
(SELECT id,
fCount,
#sum := #sum + fCount as total,
#count := #count + 1 as posts
FROM
(SELECT p.id,COUNT(fCount.id) as fCount
FROM post p
LEFT JOIN follow fCount ON fCount.user_id = p.user_id
GROUP BY p.id
)j,
(SELECT #sum:=0.0,#count:=0.0)initialize
)T
HAVING fCount >= average
sqlfiddle

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

Incorrect usage of UPDATE and ORDER BY. than it can be replaced by?

UPDATE PlayerLeague l
JOIN PlayerStat s ON l.player_id=s.player_id
SET l.league_id=8
WHERE l.league_id=2
ORDER BY s.score
DESC LIMIT 5
And I have error:
{ [Error: ER_WRONG_USAGE: Incorrect usage of UPDATE and ORDER BY]
code: 'ER_WRONG_USAGE', index: 0 }
what this mean?
I try this:
UPDATE PlayerLeague l
SET l.league_id=8
WHERE l.player_id IN
(SELECT player_id FROM PlayerStat s
JOIN PlayerLeague l ON s.player_id=l.player_id
WHERE l.league_id=2
ORDER BY s.score
DESC LIMIT 5)
but this too is wrong.
I found a solution:
UPDATE PlayerLeague l
JOIN (SELECT s.player_id
FROM PlayerStat s
JOIN PlayerLeague l
ON s.player_id=l.player_id
WHERE l.league_id=2
ORDER BY s.score DESC LIMIT 5
) AS temp
ON l.player_id=temp.player_id
SET l.league_id=8
i am not sure you intent to restrict only update few row by using LIMIT or retrieving TOP 5 playerID with high score and then updating all rows with that playerID.
if it is the first case the LIMIT should be outside the JOIN. you should check correct syntax for that. but if it is second case then here is the query.
update PlayerLeague pl
JOIN
(
select t1.player_id
from PlayerLeague t1
join PlayerStat t2
on t1.player_id=t2.player_id
and t1.league_id=2
order by t2.score desc
limit 5
)ps
ON pl.player_id=ps.player_id
set pl.league_id=8

Rank not being determined properly

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.