This question already has answers here:
Using LIMIT within GROUP BY to get N results per group?
(14 answers)
Closed 5 years ago.
This is different from the one marked as a double, I want to sum up top 5 for each team. The double post takes out for each of the results in separate rows.
I'm using this question now but it seems that SQL is randomly returning 5 of for exasmple 10 rows and sum up, not the top 5. Anyone has some input for me?
select team, sum(length) as totalScore
from
(SELECT t.*,
#num_in_group:=case when #team!=team then #num_in_group:=0 else #num_in_group:=#num_in_group+1 end as num_in_group,
#team:=team as t
FROM reg_catches t, (select #team:=-1, #num_in_group:=0) init
ORDER BY team asc) sub
WHERE sub.num_in_group<=4 and competition = 16 and team = 25
GROUP BY team
ORDER BY totalScore DESC;
I'm struggeling on a SQL question that I can't get my head around. My result-table looks like below, I'm trying to sum up the top 5 result for each team and limit the output to the top 3 highest ranked teams. Everything was working as expected until I added my last score in the result-table. The output of my SQL now is randomly for team 25. I've expected that to be 520..
team length competition
----------------------
26 70 16
25 70 16
25 95 16
25 98 16
25 100 16
25 100 16
25 100 16
25 122 16
Output:
team totalScore
---- -----------
25 122
26 70
Wanted output:
team totalScore
---- -----------
25 522
26 70
SELECT team, SUM(length) AS totalScore
FROM(
SELECT team, length
FROM table_result m
WHERE competition = 16 and (
SELECT COUNT(*)
FROM table_result mT
WHERE mT.team = m.team AND mT.length >= m.length
) <= 5) tmp
GROUP BY team
ORDER BY totalScore DESC Limit 3
Anyone has any ideas for me?
select team, sum(length)
from
(SELECT t.*,
#num_in_group:=case when #team!=team then #num_in_group:=0 else #num_in_group:=#num_in_group+1 end as num_in_group,
#team:=team as t
FROM test.table_result t, (select #team:=-1, #num_in_group:=0) init
ORDER BY team, length desc) sub
WHERE sub.num_in_group<=4
GROUP BY team
You should use a window function to accomplish this. Here's an example query:
SELECT team, SUM(length) AS totalScore FROM
(SELECT team,
length,
row_number() OVER (PARTITION BY team ORDER BY length desc) AS rowNumber
FROM table_result) tmp
WHERE rowNumber <= 5
AND competition = 16
GROUP BY team
ORDER BY totalScore DESC
LIMIT 3;
This query has two parts.
The inner query uses the row_number() window function to give every row an extra column that indicates its rank. PARTITION BY team says that the rank should be kept separately for each team, so that you end up being able to select the top n scores for every team.
The outer query uses a GROUP BY on the result of the inner query to take the SUM, per team, of all of the scores whose row number is less than or equal to 5 - in other words, the top 5 scores.
Related
I'm trying to figure out how to Select a specific number of rows from a MySQL table based on WHERE clause. I have a table with 10 dummy users, I want to get 2 previous and 2 next users of specific user with their ranks.
user_id | points
==================
10 200
4 130
2 540
13 230
15 900
11 300
3 600
17 110
20 140
1 430
5 800
I achieved adding a column for ranking like:
user_id | points | rank
===========================
15 900 1
5 800 2
3 600 3
2 540 4
1 430 5
11 300 6
13 230 7
10 200 8
20 140 9
4 130 10
17 110 11
But the problem is that I want only 5 rows. Suppose I'm retrieving data for user with user_id = 11. The output should look like this:
user_id | points | rank
===========================
2 540 4
1 430 5
11 300 6
13 230 7
10 200 8
where user_id = 11 is in the centre with 2 rows above and 2 below. I have tried nesting UNIONS and SELECT statements but nothing seems to work properly.
Here's a suggestion if you're on MySQL 8+:
WITH cte AS (
SELECT user_id, points,
ROW_NUMBER() OVER (ORDER BY points DESC) AS Rnk
FROM mytable)
SELECT cte2.user_id,
cte2.points,
cte2.Rnk
FROM cte cte1
JOIN cte cte2
ON cte1.user_id=11
AND cte2.Rnk >= cte1.Rnk-2
AND cte2.Rnk <= cte1.Rnk+2
Using common table expression (cte) then do a self join with condition of user_id=11 as base to get the Rnk value of -2 and +2.
Demo fiddle
Since you're on older MySQL version, here's what I can suggest:
SET #uid := 11;
SET #Rnk := (SELECT Rnk
FROM
(SELECT user_id, points,
#r := #r+1 AS Rnk
FROM mytable
CROSS JOIN (SELECT #r := 0) r
ORDER BY points DESC) v
WHERE user_id = #uid);
SELECT user_id, points, Rnk
FROM
(SELECT user_id, points,
#r := #r+1 AS Rnk
FROM mytable
CROSS JOIN (SELECT #r := 0) r
ORDER BY points DESC) v
WHERE Rnk >= #Rnk-2
AND Rnk <= #Rnk+2;
If you will only use user_id as base, then the only part here you need to change is the SET #uid. The remaining queries are just fulfilling your condition of getting two positions above and below the rank retrieved according to the user_id. The base query in SET #Rnk is the same as the base query for the last one. The idea is to assign #Rnk variable with Rnk position of user_id=11 then use it in WHERE condition for the last query.
I'm not aware if there's any online fiddle still using MySQL 5.1 but here's probably the closest version to it, MySQL 5.5 demo fiddle.
Using MariaDB and trying to see if I can get pull original rankings for each row of a table based on the create date.
For example, imagine a scores table that has different scores for different users and categories (lower score is better in this case)
id
leaderboardId
userId
score
submittedAt ↓
rankAtSubmit
9
15
555
50.5
2022-01-20 01:00:00
2
8
15
999
58.0
2022-01-19 01:00:00
3
7
15
999
59.1
2022-01-15 01:00:00
3
6
15
123
49.0
2022-01-12 01:00:00
1
5
15
222
51.0
2022-01-10 01:00:00
1
4
14
222
87.0
2022-01-09 01:00:00
1
5
15
555
51.0
2022-01-04 01:00:00
1
The "rankAtSubmit" column is what I'm trying to generate here if possible.
I want to take the best/smallest score of each user+leaderboard and determine what the rank of that score was when it was submitted.
My attempt at this failed because in MySQL you cannot reference outer level columns more than 1 level deep in a subquery resulting in an error trying to reference t.submittedAt in the following query:
SELECT *, (
SELECT ranking FROM (
SELECT id, RANK() OVER (PARTITION BY leaderboardId ORDER BY score ASC) ranking
FROM scores x
WHERE x.submittedAt <= t.submittedAt
GROUP BY userId, leaderboardId
) ranks
WHERE ranks.id = t.id
) rankAtSubmit
FROM scores t
Instead of using RANK(), I was able to accomplish this by with a single subquery that counts the number of users that have a score that is lower than and submitted before the given score.
SELECT id, userId, score, leaderboardId, submittedAt,
(
SELECT COUNT(DISTINCT userId) + 1
FROM scores t2
WHERE t2.userId = t.userId AND
t2.leaderboardId = t.leaderboardId AND
t2.score < t.score AND
t2.submittedAt <= t.submittedAt
) AS rankAtSubmit
FROM scores t
What I understand from your question is you want to know the minimum and maximum rank of each user.
Here is the code
SELECT userId, leaderboardId, score, min(rankAtSubmit),max(rankAtSubmit)
FROM scores
group BY userId,
leaderboardId,
scorescode here
I have a Mysql table with users playing as double partners in a tennis tournament and also a table with rankings in which every player has a ranking score in doubles matches.
Tournaments_registrations_doubles
id
player1_id
player2_id
1
32
25
2
25
28
3
143
83
Rankings_doubles
id
player_id
points
1
25
127
2
19
83
3
32
97
4
83
41
I am trying to build an SQL query that will get the ids of the Tournaments_registrations_doubles table in descending order based on the sum of the ranking points of each player group. So player_1 and player_2 who's sum of point in rankings table is highest should come first, and so on. Not all players are registered in the ranking table.
Any ideas on how to implement this?
Join the tournament table to the doubles table twice, once for each of the two player columns. Then order the result set descending by the sum of points.
SELECT t.id, t.player1_id, t.player2_id,
COALESCE(r1.points, 0) + COALESCE(r2.points, 0) AS total
FROM Tournaments_registrations_doubles t
LEFT JOIN Rankings_doubles r1 ON r1.player_id = t.player1_id
LEFT JOIN Rankings_doubles r2 ON r2.player_id = t.player2_id
ORDER BY COALESCE(r1.points, 0) + COALESCE(r2.points, 0) DESC;
Notice that we use left joins here, so that we may treat missing points as zero when computing the totals.
I'm trying to make a standings chart using multiple tables. One keeps tracks of meetings attended to, each meeting counting as 5 points and the other table keeps track of results of tournaments. This is a fishing club site.
I have the following so far and can show the meeting points in order but the tournament results separate from that. I'd like to find a single albeit complex SQL statement to list out current standings.
I need to show the angler name which I can grab separate from a different table, then each month's 5 points listed along with the tournament result amount from the results table, these are all added up to finally list the total from all tournaments and meetings.
SELECT aid, sum(here*5) as total
FROM rollcall GROUP BY aid ORDER BY total DESC
SELECT aid, weight, weight-penalty as fweight
FROM `results` where tid=2 order by fweight desc
So an example is:
place angler JAN FEB MARCH ... Total Points
1 name1 5 50 5 45 0 38 143
2 name2 5 49 5 47 5 31 142
...
Is that clear at all?
What if you build your query something like this?
SELECT aid,
sum(SELECT count(1) from meetings WHERE MONTH(columndatetime) = 1 * 5) AS JAN,
sum(SELECT count(1) from meetings WHERE MONTH(columndatetime) = 2 * 5) AS FEB,
/
-- add same logic to the rest of the months
/
sum(SELECT count(1) from meetings WHERE YEAR(columndatetime) = 2013 * 5) as total
FROM rollcall GROUP BY aid ORDER BY total DESC
Where columndatetime is the name of your column that has the date and time for the meetings etc...
The last one takes all for the year.
Could this help you out?
I have a table:
quiz userid attempt grade
1 3 1 33
1 3 2 67
1 3 3 90
1 3 4 20
Now, I want the last two attempts i.e., 4 and 3 and I want average grade of these 2 grades i.e, 90 and 20
Could anyone help me?
Use ORDER and LIMIT to get the 2 last attempts and the AVG aggregation function :
SELECT AVG(grade) AS average FROM (
SELECT grade FROM table
WHERE userid = 3
ORDER BY attempt DESC LIMIT 2) AS t
If you want to list both test results separately, with the average in each row, then something like this maybe (otherwise you just need the subquery for the average of the two tests):
SELECT userid, attempt, grade,
( SELECT AVG(grade)
FROM table
ORDER BY attempt DESC LIMIT 0, 2 ) AS avg_grade
FROM table
ORDER BY attempt DESC LIMIT 0, 2;