mySQL "Rank in Highscore"-Query - php

Hi there coders around the world,
I'm working on a project where users can do certain things and gain points for it. To simplify this question let's say we got 2 tables user and points.
-- table user -- table points
+---------------+ +-----------------------------+
| id | name | | id | points | user_id |
+---------------+ +-----------------------------+
| 1 Tim | | 1 5 1 |
| 2 Tom | | 2 10 1 |
| 3 Marc | | 3 5 1 |
| 4 Tina | | 4 12 2 |
| 5 Lutz | | 5 2 2 |
+---------------+ | 6 7 1 |
| 7 40 3 |
| 8 100 1 |
+-----------------------------+
Now to get the complete highscore-list I use the following query
SELECT u.*, SUM( p.points ) AS sum_points
FROM user u
LEFT JOIN points p ON p.user_id = u.id
GROUP BY u.id
ORDER BY sum_points DESC
resulting in a fine highscore-list with all users from first to last
+------------------------------+
| id | name | sum_points |
+------------------------------+
| 1 Tim 127 |
| 3 Marc 40 |
| 2 Tom 14 |
| 4 Tina 0 |
| 5 Lutz 0 |
+------------------------------+
Alright back to the question itself. On the profile of a single user I'd like to show his ranking within the highscore-list.
Can this be done using a single query just showing that for example Tom (id=2) is ranked in place 3?
Thanks alot :-)

The idea is to ask, "how many players rank above #this_user":
select count(*) + 1 from
(
/* list of all users */
SELECT SUM( p.points ) AS sum_points
FROM user u
LEFT JOIN points p ON p.user_id = u.id
GROUP BY u.id
) x
/* just count the ones with higher sum_points */
where sum_points > (select sum(points) from points where user_id = #this_user)
Edited to make result 1-based instead of 0-based

SELECT q.*,
#r := #r + 1 AS rank
FROM (
SELECT #r := 0
) vars,
(
SELECT u.*,
SUM(p.points) AS sum_points
FROM
user u
LEFT JOIN
points p
ON p.user_id = u.id
GROUP BY
u.id
ORDER BY
sum_points DESC
) q

Related

Get rows above and below (neighbouring rows) a certain row, based on two criteria SQL

Say I have a table like so:
+---+-------+------+---------------------+
|id | level |score | timestamp |
+---+-------+------+---------------------+
| 4 | 1 | 70 | 2021-01-14 21:50:38 |
| 3 | 1 | 90 | 2021-01-12 15:38:0 |
| 1 | 1 | 20 | 2021-01-14 13:10:12 |
| 5 | 1 | 50 | 2021-01-13 12:32:11 |
| 7 | 1 | 50 | 2021-01-14 17:15:20 |
| 8 | 1 | 55 | 2021-01-14 09:20:00 |
| 10| 2 | 99 | 2021-01-15 10:50:38 |
| 2 | 1 | 45 | 2021-01-15 10:50:38 |
+---+-------+------+---------------------+
What I want to do is show 5 of these rows in a table (in html), with a certain row (e.g. where id=5) in the middle and have the two rows above and below it (in the correct order). Also where level=1. This will be like a score board but only showing the user's score with the two above and two below.
So because scores can be the same, the timestamp column will also need to be used - so if two scores are equal, then the first person to get the score is shown above the other person.
E.g. say the user is id=5, I want to show
+---+-------+------+---------------------+
|id | level |score | timestamp |
+---+-------+------+---------------------+
| 4 | 1 | 70 | 2021-01-14 21:50:38 |
| 8 | 1 | 55 | 2021-01-14 09:20:00 |
| 5 | 1 | 50 | 2021-01-13 12:32:11 |
| 7 | 1 | 50 | 2021-01-14 17:15:20 |
| 2 | 1 | 45 | 2021-01-15 10:50:38 |
| 1 | 1 | 20 | 2021-01-14 13:10:12 |
+---+-------+------+---------------------+
Note that id=7 is below id=5
I am wondering does anyone know a way of doing this?
I have tried this below but it is not outputting what I need (it is outputting where level_id=2 and id=5, and the other rows are not in order)
((SELECT b.* FROM table a JOIN table b ON b.score > a.score OR (b.score = a.score AND b.timestamp < a.timestamp)
WHERE a.level_id = 1 AND a.id = 5 ORDER BY score ASC, timestamp DESC LIMIT 3)
UNION ALL
(SELECT b.* FROM table a JOIN table b ON b.score < a.score OR (b.score = a.score AND b.timestamp > a.timestamp)
WHERE a.level_id = 1 AND a.id = 5 ORDER BY score DESC, timestamp ASC LIMIT 2))
order by score
If it is easier to output all rows in the table, say where level = 1, so it is a full score board.. and then do the getting a certain row and two above and below it using PHP I'd also like to know please :) ! (possibly thinking this may keep the SQL simpler)?
You can use cte and inner join as follows:
With cte as
(select t.*,
dense_rank() over (order by score) as dr
from your_table t)
Select c.*
From cte c join cte cu on c.dr between cu.dr - 2 and cu.dr + 2
Where cu.id = 5
Ordwr by c.dr, c.timestamp
I would suggest window functions:
select t.*
from (select t.*,
max(case when id = 7 then score_rank end) over () as id_rank
from (select t.*,
dense_rank() over (order by score) as score_rank
from t
where level = 1
) t
) t
where score_rank between id_rank - 2 and id_rank + 2;
Note: This returns 5 distinct score values, which may result in more rows depending on duplicates.
Here is a db<>fiddle.
EDIT:
If you want exactly 5 rows using the timestamp, then:
select t.*
from (select t.*,
max(case when id = 7 then score_rank end) over () as id_rank
from (select t.*,
dense_rank() over (order by score, timestamp) as score_rank
from t
where level = 1
) t
) t
where score_rank between id_rank - 2 and id_rank + 2
order by score;
Note: This still treats equivalent timestamps as the same, but they seem to be unique in your data.

php join and count then group and order by

I have two tables;
Users
id | username | club
-----------------------
1 | James5 | 2
2 | 007 | 1
3 | xmen | 2
4 | terminator | 2
suggestedusers
id | username | club
----------------------
1 | mark | 2
2 | bon | 1
3 | hero | 2
4 | scorpio | 2
5 | lame | 5
How do I join these tables to get the total of the clubs? e.g an answer like
club | clubCount
-------------------
2 | 6
1 | 2
5 | 1
I was thinking of the following query;
SELECT User.club, COUNT(User.club) + COUNT(suggestedusers.club) AS clubCount FROM User, suggestedusers
GROUP BY User.club
ORDER BY clubCount DESC
But the above script is not working.
Your target result is not clear to me.
You can try with this one
Select club, count(club) as clubcount from(
select users.id, users.username, users.club from users
UNION
select suggestedusers.id, suggestedusers.username,
suggestedusers.club from suggested users ) group by club, clubcount Order by
clubcount desc;
Assuming what you want is to get the number of users per club for both tables, this should work for you:
SELECT DISTINCT COALESCE(u.club, su.club) AS club
, COALESCE(count(u.club), count(su.club)) AS clubcount
FROM Users u
INNER JOIN Suggestedusers su ON su.club = u.club
GROUP BY u.club,su.club
ORDER BY clubcount DESC
Please check the names of tables and columns when using in your code.

Record that haven't relationship with other table

i have two tables: users and user_visits.
For example i would get the user_id and name of users that have 0 visited where date > '2014-05-06' and date < '2014-05-11'.
USERS TABLE USERS_VISITS TABLE
USER_ID | NAME USERS_VISIT_ID | USER_ID | DATE
1 | John 1 | 1 | 2014-05-07
2 | Mark 2 | 1 | 2014-05-08
3 | Mike 3 | 4 | 2014-05-10
4 | Steven
The result must be:
USER_ID | NAME | COUNT(*) |
2 | Mark | 0 |
3 | Mike | 0 |
My query is:
SELECT COUNT(uv.user_id) AS count_visited,
FROM users u
LEFT JOIN users_visited uv ON (u.user_id=uv.user_id)
WHERE uv.date >= '2014-05-06'
AND uv.date <= '22014-05-11'
GROUP BY u.user_id
HAVING count_visited = 0;
This is how you can do it using left join
select
u.user_id,
u.name
from user u
left join user_visits uv on uv.user_id = u.user_id
AND uv.date >= '2014-05-06'
AND uv.date <= '22014-05-11'
where uv.user_id is NULL

MYSQL SELECT rank of user (more than x & less than y)

I'm a bit stuck with a php/Mysql Query. I got 2 tables :
table_users table_ranks
---------------- ------------------------
| id | points | | name | points_needed |
---------------- ------------------------
| 1 | 2 | | lvl0 | 0 |
| 2 | 10 | | lvl1 | 10 |
| 3 | 21 | | lvl2 | 20 |
| 4 | 29 | | lvl3 | 30 |
---------------- ------------------------
I need an ouput like this :
User_1 = lvl0 (because user has 2 points)
User_2 = lvl1 (because user has just reached 10 points)
...
User_4 = lvl2 (because user has not yet reached 30 points)
Think you :)
Regards.
You can do it like this
SELECT
tu.id,
tr.name,
tu.points
FROM table_ranks as tr
LEFT JOIN (SELECT * FROM table_ranks LIMIT 1,69596585953484) as l
ON l.points_needed = (SELECT MIN(points_needed) FROM table_ranks WHERE points_needed > tr.points_needed limit 1)
LEFT OUTER JOIN table_users AS tu ON tu.points >= tr.points_needed AND tu.points < l.points_needed
WHERE tu.id IS NOT NULL
group by tu.id
Fiddle
Output
-------------------------
| id | points | name |
-------------------------
| 1 | lvl0 | 2 |
| 2 | lvl1 | 10 |
| 3 | lvl2 | 21 |
| 4 | lvl2 | 29 |
-------------------------
give this a try, a little bit messy due to table design,
SELECT u.*, r.name
FROM table_users u
INNER JOIN
(
SELECT x.name,
x.points_needed start_point,
COALESCE(y.points_needed - 1, 2000000) end_point
FROM
(
SELECT name, points_needed, #rank:=#rank+1 ranks
FROM table_ranks a, (SELECT #rank:=0) b
ORDER BY points_needed
) x
LEFT JOIN
(
SELECT name, points_needed, #rank1:=#rank1+1 ranks
FROM table_ranks a, (SELECT #rank1:=0) b
ORDER BY points_needed
) y ON x.ranks+1 = y.ranks
) r ON u.points BETWEEN r.start_point AND r.end_point
SQLFiddle Demo

get highest and sum of values from two table

I Want to show accounts with their highest rank characters and SUM of Their Time Where stats on table accounts is 1 with just in one Query.
I have Two Table with below data :
Table: Accounts:
Id| Username |Stats
1 | player1 |1
2 | goodman |1
3 | goodbat |1
4 | ashasdd |0
Table: Characters:
Guid| Account | Name | Rank | Time |
213 | 1 | fres | 2 | 51 |
214 | 2 | sdg2 | 3 | 12 |
215 | 2 | fgax | 4 | 99 |
216 | 3 | zFvx | 8 | 23 |
217 | 3 | Sgzs | 2 | 13 |
Output/Result: (Show Accounts characters with their Highest rank character and Their Sum of time)
Username : player1 | Name: fres(Rank:2) |Time : 51
Username : goodman | Name: fgax(Rank:4) |Time : 111
Username : goodbat | Name: zFvx(Rank:8) |Time : 36
what's the simple MySQL Query ?
My bad Query: (don't work)
SELECT a.username, a.email, c.name, SUM(c.time), c.rank
FROM `auth`.`account` a, `characters`.`characters` c
WHERE a.id=c.account
ORDER BY c.rank ASC
LIMIT 20
should be enough.. i think.
SELECT c.Account, a.Username, c.Name, MAX(c.Rank) as maxrank, SUM(c.TIME) as sumtime FROM characters c LEFT JOIN Accounts a ON a.Id=c.Account GROUP By c.Account;
Something like this might work
SELECT username, name, d.rank, c.Time
FROM (
SELECT a.Username, b.account, MAX( b.Rank ) AS rank, SUM( b.Time ) AS TIME
FROM accounts a
INNER JOIN characters b ON a.ID = b.Account
WHERE a.stats =1
GROUP BY a.Username
) AS c
JOIN characters d ON d.account = c.account
AND d.rank = c.rank
ORDER BY rank
Not sure how to do it in just one select tho since then you might get the wrong name, thats not registered with the max rank.

Categories