This question already has answers here:
MySQL's alternative to T-SQL's WITH TIES
(3 answers)
Closed 9 years ago.
I'm building a "Top 3 Scores" Leaderboard. I want to display the top three scores, drawing the max per person, but I don't want to limit 3, because I want to display anyone that has the top 3 scores. So for example, with the data below,
+----+-----+
|Name|Score|
+----+-----+
|Matt| 17|
|Mark| 29|
|Luke| 28|
|John| 29|
|Paul| 27|
|Matt| 29|
|Mark| 22|
+----+-----+
I want to display:
+------+-----+
|Name |Score|
+------+-----+
|1.Matt| 30|
|2.Mark| 29|
|2.John| 29|
|3.Luke| 28|
+------+-----+
My first thought is to extract the max for everyone, and then stop displaying after the score changes (using PHP).
select name, max(score)
from SCORES
group by name
order by name
Is there any way to do this directly in SQL?
SELECT name, score
FROM SCORES
JOIN (SELECT distinct score score3
FROM scores
ORDER BY score DESC
LIMIT 2, 1) x
ON score >= score3
ORDER by score DESC
FIDDLE
SELECT name,score
FROM SCORES
WHERE score in (
SELECT distinct s.score
FROM SCORES as s
ORDER BY s.score desc
LIMIT 3)
)
ORDER BY score
SELECT Name, MAX(Score) Score
FROM TableName a
WHERE EXISTS
(
SELECT 1
FROM TableName b
WHERE a.Score = b.Score
GROUP BY Score
ORDER BY Score DESC
LIMIT 3
)
GROUP BY Name
ORDER BY Score DESC
SQLFiddle Demo
OUTPUT based on the records given above
╔══════╦═══════╗
║ NAME ║ SCORE ║
╠══════╬═══════╣
║ Mark ║ 29 ║
║ John ║ 29 ║
║ Matt ║ 29 ║
║ Luke ║ 28 ║
║ Paul ║ 27 ║
╚══════╩═══════╝
this will give the top three scores, regardless of ties.
SELECT score FROM mytable group by score
ORDER BY score DESC
LIMIT 3
now get the third score.
SELECT MIN(score') FROM (SELECTscoreFROMmytablegroup by score
ORDER BYscore` DESC
LIMIT 3) as top3
finally get everything equal or above the third score
SELECT * FROM mytable WHERE score' >=
(SELECT MIN(score') FROM
(SELECT score FROM mtyable group by score'
ORDER BYscore` DESC
LIMIT 3) as top3)
I would try something like:
SELECT * FROM mytable
WHERE score IN (SELECT DISTINCT score FROM mytable ORDER BY score DESC LIMIT 3)
ORDER BY score DESC
Here IS SQL FIDDLE.
SELECT
NAME,
score
FROM SCORES
WHERE FIND_IN_SET(score,(SELECT
SUBSTRING_INDEX(GROUP_CONCAT( DISTINCT score ORDER BY score DESC),',',3)
FROM SCORES)) > 0
ORDER BY score desc
Related
I have been given a leaderboard, I need to return the row correspond to 4th to 8th rank in a leaderboard?
id name score
1 gongy 3001
2 urandom 2401
3 eduardische 2477
4 Gassa 2999
5 bcc32 2658
6 Alex_2oo8 6000
7 mirosuaf 2479
8 Sparik 2399
9 thomas_holmes 2478
The query I have is not being accepted
select b.name from
(
select a.name as name ,a.score as score from
(
select name,score from leaderboard order by score desc limit 8
)a
order by a.score
limit 5
) b
order by b.score desc;
You can use LIMIT OFFSET
select id,
name,
score
from leaderboard
order by score desc
limit 4 offset 3 ; --- fetch 4 records, begin with record 4 (OFFSET 3)
https://dbfiddle.uk/Ys-3jC4L
Above query skips the first 4 rows and limit the result to 4 make it from 4th to 8th
select x.id,x.name,x.score
from
(
select id,name,score,
row_number()over(order by score desc)xcol
from your_table
)x where x.xcol >=3 and x.xcol<=8
May be something like this ?
I have a table called 'scorelist' with the following results:
ID USER_ID SCORE SEASON
-----------------------------
1 1 35 3
2 1 45 2
3 2 80 3
4 2 85 1
5 3 65 2
I want to make a score list where I show the scores of the users but only of their last played season.
Result should be:
ID USER_ID SCORE SEASON
-----------------------------
3 2 80 3
5 3 65 2
1 1 35 2
I use the following code:
SELECT * FROM scorelist
WHERE season = (
SELECT season FROM scorelist ORDER BY season DESC LIMIT 1
)
GROUP BY user_id
ORDER BY score DESC;
But then I only get the results of season 3, so a lot of users are not shown.
I also tried:
SELECT * FROM scorelist group by user_id ORDER BY score DESC, season DESC
But this is also not working.
I hope you can help me.
The subquery gets the latest season for each user. If you join to that you get your desired results
SELECT s1.*
FROM scorelist s1
JOIN
(
SELECT user_id, max(season) AS season
FROM scorelist
GROUP BY user_id
) s2 ON s1.user_id = s2.user_id AND s1.season = s2.season
Since MySQL 8.0 you can use window function row_number to solve this problem:
WITH ordered_scorelist AS (
SELECT
scorelist.*,
row_number() over (partition by USER_ID order by SEASON DESC) rn
FROM scorelist
) SELECT
USER_ID, SCORE, SEASON
FROM ordered_scorelist
WHERE rn = 1
ORDER BY SCORE DESC;
MySQL row_number test
I have a table with people, their age, and their awesomeness at each age.
What is the simplest query to get John's 'awesomeness' at their maximum age?
People
Name Age Awesomeness
Don 1 12
Don 2 23
Don 3 43
Don 4 30
Sam 1 9
Sam 2 18
Sam 3 59
Sam 4 99
The best query I have:
SELECT awesomeness
FROM people
JOIN (
SELECT MAX(age)
FROM people
WHERE name = 'Don'
) a
ON people.age = a.age
WHERE people.name = 'Don'
Just use order by and limit:
SELECT awesomeness
FROM people
WHERE people.name = 'Don'
ORDER BY age desc
LIMIT 1
You may be wishing to show everyone's score at their maximum age.
You can do that with this query: http://sqlfiddle.com/#!2/b0ff4/1/0
SELECT a.name, a.awesomeness
FROM people a
JOIN (
SELECT name, MAX(age) age
FROM people
GROUP by name
) b ON a.name = b.name AND a.age=b.age
I work with mysql and I've never encountered such a great challenge.
I hope you can help..
I have 1 table called Reports:
ID SerialNumber Remain_Toner_Black
7099 Z5UEBJAC900002Y 37
7281 Z5UEBJAC900002Y 36
7331 Z5UEBJAC900002Y 100
7627 8Z37B1DQ100105N 58
7660 8Z37B1DQ100105N 57
5996 CND8DDM2FH 83
5971 CND8DDM2FH 83
7062 3960125290 0
7088 3960125290 93
7100 3960125290 100
Now I want to be able to select records from the table where Remain_Toner_Black is higher than Remain_Toner_Black of the previous row in the table with the same SerialNumber (by previous I mean lower ID with the same SerialNumber).
For the above records I want the results below:
ID SerialNumber Remain_Toner_Black_Before Remain_Toner_Black_After
7331 Z5UEBJAC900002Y 36 100
7088 3960125290 0 93
7100 3960125290 93 100
SELECT a.ID, a.SerialNumber,
b.Remain_Toner_Black BeforeCount,
a.Remain_Toner_Black AfterCount
FROM
(
SELECT A.ID,
A.SerialNumber,
A.Remain_Toner_Black,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.SerialNumber = a.SerialNumber AND
c.ID <= a.ID) AS RowNumber
FROM TableName a
) a
INNER JOIN
(
SELECT A.ID,
A.SerialNumber,
A.Remain_Toner_Black,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.SerialNumber = a.SerialNumber AND
c.ID <= a.ID) AS RowNumber
FROM TableName a
) b ON a.SerialNumber = b.SerialNumber AND
a.RowNumber = b.RowNumber + 1
WHERE b.Remain_Toner_Black < a.Remain_Toner_Black
SQLFiddle Demo
OUTPUT
╔══════╦═════════════════╦═════════════╦════════════╗
║ ID ║ SERIALNUMBER ║ BEFORECOUNT ║ AFTERCOUNT ║
╠══════╬═════════════════╬═════════════╬════════════╣
║ 7331 ║ Z5UEBJAC900002Y ║ 36 ║ 100 ║
║ 7088 ║ 3960125290 ║ 0 ║ 93 ║
║ 7100 ║ 3960125290 ║ 93 ║ 100 ║
╚══════╩═════════════════╩═════════════╩════════════╝
BRIEF EXPLANATION
What the query above does is it generates a sequential number which mimics ROW_NUMBER() on other RDBS for every SerialNumber ordered by ID in ascending order.
The two subquery is then joined via the SerialNumber and sequential number generated. On the generated number, the value on the first subquery must be equal to plus one of the value on the second subquery to get the number of toner on the next reord.
MySQL (or indeed most other RDBMS) doesn't easily allow cross-row comparisons.
To do this sort of query, you must either perform a very expensive join comparing one version of the table to several rows of another version of the table, or construct a complex expression using user variables.
For example (with user variables):
SELECT ID, SerialNumber, #x Remain_Toner_Before,
#x := Remain_Toner_Black AS Remain_Toner_After
FROM Reports, (SELECT #x := -4) x
WHERE Remain_Toner_Black > #x
AND SerialNumber = '3960125290'
ORDER BY ID;
(the -4 is from your comment to another answer)
A better solution is to do this with cursors or in your application code, where you need only do one pass and compare simple logic by using variables in the cursor or application code.
I am using MySQL, I have 50 records in employee table. I want to find the person with the 22nd highest salary.
Use LIMIT, specifying both an offset and a row count.
To get the 22nd ranked person in order of highest salary, do:
SELECT person
FROM employee
ORDER BY salary DESC
LIMIT 21, 1
Notice the use of 21 here. This is because the offset of the initial row (1st highest salary) is actually 0. Therefore the 22nd highest salary will actually be an offset of 21 (the 21st row in 0-based counting, or "skip 21 rows").
To get the person(s) with the 22nd highest salary, you will need one more level of indirection. Try:
SELECT person
FROM employee
WHERE salary = (
SELECT DISTINCT salary
FROM employee
ORDER BY salary DESC
LIMIT 21, 1
)
here's another one, considering you have duplicate salary number. I guess limit won't correct solve your case if you have some duplicates. Try something like this,
SELECT aa.*
FROM table1 aa
INNER JOIN
(
SELECT #row:=#row+1 rankNo,
a.Salary
FROM (SELECT DISTINCT Salary FROM table1) a,
(SELECT #row:=0) s
ORDER BY Salary DESC
) bb ON aa.Salary = bb.Salary AND
bb.rankNo = 2
SQLFiddle Demo
consider you have records like this,
CREATE TABLE Table1
(`EmpID` int, `Salary` int);
INSERT INTO Table1
(`EmpID`, `Salary`)
VALUES
(1, 10),
(2, 12), -- duplicate
(3, 11),
(4, 12), -- duplicate
(5, 14),
(6, 12); -- duplicate
╔═══════╦════════╗
║ EMPID ║ SALARY ║
╠═══════╬════════╣
║ 1 ║ 10 ║
║ 2 ║ 12 ║
║ 3 ║ 11 ║ -- you want to get this value (*2nd from the last value*)
║ 4 ║ 12 ║
║ 5 ║ 14 ║
║ 6 ║ 12 ║
╚═══════╩════════╝
SELECT MIN(salary) FROM (
SELECT DISTINCT salary FROM employee ORDER BY salary DESC LIMIT 22
) limited_salary
Just answered a similar question here: select all rows except the four most recent
In your case, you'll want to LIMIT to 1, and OFFSET to the 22 position.
LIMIT 21,1
How to find n'th highest value of a column?
Query to find nth max value of a column