I'm trying to get my head around a query and I just can't figure it out. I would appreciate if someone give me a pointer. As a simple example of what I'm trying to achieve, I have these records in the database
Score|Ranking
-------------
100 |0
200 |0
300 |0
And I would like the Ranking field to contain 1,2,3 based on who's got the highest score so the result should be:
Score|Ranking
-------------
100 |3
200 |2
300 |1
At the moment, I'm doing a for next loop for all these records but given that in reality that could be a few thousand - that could take forever! Does anyone have an idea on a magic query which would do this in one go?
Here's a way to do it:
SET #r=0;
UPDATE table SET Ranking= #r:= (#r+1) ORDER BY Score DESC;
/* use this if you just want to pull it from the db, but don't update anything */
SET #r=0;
SELECT *, #r:= (#r+1) as Ranking FROM table ORDER BY Score DESC;
In MySQL, you can use row_number.
Here's an example of using it in a SELECT:
select #rownum:=#rownum+1 ‘rank’, p.*
from player p, (SELECT #rownum:=0) r
order by score desc;
If you INSERT INTO using a SELECT like this, you will get your rankings.
This creates an inline update statement that will rank your players incrementing by the variable #rc. I've used it many times in very similar cases, it works well and keeps it all on the DB side.
SET #rc = 0;
UPDATE players JOIN (SELECT #rc := #rc + 1 AS rank, id FROM players ORDER BY rank DESC)
AS order USING(id) SET players.rank = order.rank;
id is assumed to be the primary key for your players table.
If you are using MySQL 8 so can you use the new function RANK()
SELECT
score,
RANK() OVER (
ORDER BY score DESC
) ranking
FROM
table;
Depending on how you want to display the ranking with even score so can you also check out DENSE_RANK()
And as an UPDATE:
WITH
ranking AS(
SELECT
score,
RANK() OVER (
ORDER BY score DESC
) ranking
FROM
table
)
UPDATE
table,
ranking r
SET
table.ranking = r.ranking
WHERE
table.score = r.score
SET #r = 0;
UPDATE players JOIN (SELECT #r := #r + 1 AS rank, id FROM players ORDER BY rank DESC)
AS sorted USING(id) SET players.rank = sorted.rank;
i'm showing you my way of doing it [for interval sql update functions]
select:
set #currentRank = 0,
#lastRating = null,
#rowNumber = 1;
select
*,
#currentRank := if(#lastRating = `score`, #currentRank, #rowNumber) `rank`,
#rowNumber := #rowNumber + if(#lastRating = `score`, 0, 1) `rowNumber`,
#lastRating := `score`
from `table`
order by `score` desc
update:
set #currentRank = 0,
#lastRating = null,
#rowNumber = 1;
update
`table` r
inner join (
select
`primaryID`,
#currentRank := if(#lastRating = `score`, #currentRank, #rowNumber) `rank`,
#rowNumber := #rowNumber + if(#lastRating = `score`, 0, 1) `rowNumber`,
#lastRating := `score`
from `table`
order by `score` desc
) var on
var.`primaryID` = r.`primaryID`
set
r.`rank` = var.`rank`
i did not make any performance checks on this one except for testing that it works
Related
i am very new to mysql and tried to rank top records based on two fields
sharing you the current script and output along with the desired output
Current Script :
select u_rank, c_rank,u_name,c_name,
#curRank := #curRank + 1 AS rank
from (
SELECT u_rank, c_rank,u_name,c_name
from abc
) a,
(
select #curRank := 0
) r
order by c_rank,u_rank
Current OutpuT
MY DESIRED OUTPUT IS
Please help
If I am decifering your question correctly, you are wanting to rank the c_ranks ascending or each u_name and then order by that rank then the u_rank. After that you need to get a rank for all records in that order. I'm not sure that the way I just worded that made any sense... but the following should get that result for you:
SET #rank1 = 0;
SET #rank2 = 0;
SET #u_name = '';
SELECT u_rank, c_rank, u_name, c_name, #rank2 := #rank2 + 1 AS rank
FROM (
SELECT u_rank,
c_rank,
c_name,
#rank1 := (#rank1 + 1 - IF(#u_name = u_name, 0, #rank1)) AS rank1,
#u_name := u_name AS u_name
FROM abc
ORDER BY u_name, c_rank
) foo
ORDER BY rank1, u_rank;
I am using a modified version of a query similiar to another question here:Convert SQL Server query to MySQL
Select *
from
(
SELECT tbl.*, #counter := #counter +1 counter
FROM (select #counter:=0) initvar, tbl
Where client_id = 55
ORDER BY ordcolumn
) X
where counter >= (80/100 * #counter);
ORDER BY ordcolumn
tbl.* contains the field 'client_id' and I am attempting to get the top 20% of the records for each client_id in a single statement. Right now if I feed it a single client_id in the where statement it gives me the correct results, however if I feed it multiple client_id's it simply takes the top 20% of the combined recordset instead of doing each client_id individually.
I'm aware of how to do this in most databases, but the logic in MySQL is eluding me. I get the feeling it involves some ranking and partitioning.
Sample data is pretty straight forward.
Client_id rate
1 1
1 2
1 3
(etc to rate = 100)
2 1
2 2
2 3
(etc to rate = 100)
Actual values aren't that clean, but it works.
As an added bonus...there is also a date field associated to these records and 1 to 100 exists for this client for multiple dates. I need to grab the top 20% of records for each client_id, year(date),month(date)
You need to do the enumeration for each client:
SELECT *
FROM (SELECT tbl.*, #counter := #counter +1 counter
(#rn := if(#c = client_id, #rn + 1,
if(#c := client_id, 1, 1)
)
)
FROM (select #c := -1, #rn := 0) initvar CROSS JOIN tbl
ORDER BY client_id, ordcolumn
) t cross join
(SELECT client_id, COUNT(*) as cnt
FROM tbl
GROUP BY client_id
) tt
where rn >= (80/100 * tt.cnt);
ORDER BY ordcolumn;
Using Gordon's answer as a starting point, I think this might be closer to what you need.
SELECT t.*
, (#counter := #counter+1) AS overallRow
, (#clientRow := if(#prevClient = t.client_id, #clientRow + 1,
if(#prevClient := t.client_id, 1, 1) -- This just updates #prevClient without creating an extra field, though it makes it a little harder to read
)
) AS clientRow
-- Alteratively (for everything done in clientRow)
, #clientRow := if(#prevClient = t.client_id, #clientRow + 1, 1) AS clientRow
, #prevClient := t.client_id AS extraField
-- This may be more reliable as well; I not sure if the order
-- of evaluation of IF(,,) is reliable enough to guarantee
-- no side effects in the non-"alternatively" clientRow calculation.
FROM tbl AS t
INNER JOIN (
SELECT client_id, COUNT(*) AS c
FROM tbl
GROUP BY client_id
) AS cc ON tbl.client_id = cc.client_id
INNER JOIN (select #prevClient := -1, #clientRow := 0) AS initvar ON 1 = 1
WHERE t.client_id = 55
HAVING clientRow * 5 < cc.c -- You can use a HAVING without a GROUP BY in MySQL
-- (note that clientRow is derived, so you cannot use it in the `WHERE`)
ORDER BY t.client_id, t.ordcolumn
;
I have table with id (store user id) and score in different match. I want what is the position of a user.
So for i try this sql fiddle;
in this I am getting all the row but I need only user having id 3 and it position in the table.
like this:
Score Postion
26 3
Even i try to do like this but no success
MySql: Find row number of specific record
With MySQL, how can I generate a column containing the record index in a table?
I got the answer: http://sqlfiddle.com/#!2/b787a/2
select * from (
select T.*,(#rownum := #rownum + 1) as rownum from (
select sum(score) as S,id from mytable group by id order by S desc ) as T
JOIN (SELECT #rownum := 0) r
) as w where id = 3
Updated sqlfiddle and above query. Now it is working perfectly.
I think this should do the trick:
SELECT totalScore, rownum FROM (
SELECT id,sum(score) AS totalScore,(#rownum := #rownum + 1) AS rownum
FROM mytable
JOIN (SELECT #rownum := 0) r
group by id) result
WHERE result.ID = 3;
just add a where clause
select x.id,x.sum,x.rownum
from(
select id,sum(score) as sum,(#rownum := #rownum + 1) as rownum
from mytable
JOIN (SELECT #rownum := 0) r
group by id
) x
where id =3
I am trying to do something like this in MYSQL, but without making query multiple times (50 times, in my case) through a PHP foreach.
foreach($this->map_ids as $key => $val) {
$this->db->query("SELECT scores.profile_number, scores.score FROM scores
LEFT JOIN players ON scores.profile_number = players.profile_number
WHERE scores.map_id = {'$val'}
AND scores.profile_number IN (SELECT profile_number FROM players WHERE banned = 0) LIMIT 10");
}
This is how it looks approximately when I retrieve all scores without LIMIT.
profile score map_id
76561198026851335 2478 47455
76561198043770492 2480 47455
... ... ...
76561198043899549 1340 47452
76561198048179892 1345 47452
... ... ...
I want only 10 entries (scores) from each unique map_id.
This is surprisingly difficult to do but I've ended up using user variables to do the job, check out the following demo. Obviously my data structure is much simplified but it should be enough to get you going:
SQL Fiddle example
Here is the SQL for anyone who may be interested in skipping the demo (hideous, I know)
SELECT *
FROM (
SELECT profile_number, score, map_id
FROM (
SELECT
profile_number, score, map_id,
IF( #prev <> map_id, #rownum := 1, #rownum := #rownum+1 ) AS rank,
#prev := map_id
FROM scores
JOIN (SELECT #rownum := NULL, #prev := 0) AS r
ORDER BY map_id
) AS tmp
WHERE tmp.rank <= 10
) s
JOIN players p
ON s.profile_number = p.profile_number
Basically, what is happening is this:
ORDER BY map_id
Orders your table by map_id so that all the same ones are together.
Next we assign a rownumber to each row by using the following logic:
IF( #prev <> map_id, #rownum := 1, #rownum := #rownum+1 )
If the previous row's map_id is not equal to the current row's ID, set the row number = 1, otherwise increase the rownumber by 1.
Finally, only return the rows who have a rownumber less than or equal to 10
WHERE tmp.rank <= 10
Hope that makes it a little clearer for you.
You can use the limit directive.
SELECT * FROM `your_table` LIMIT 0, 10
This will display the first 10 results from the database.
SELECT * FROM `your_table` LIMIT 5, 5
This will show records 6, 7, 8, 9, and 10
I have a MySQL table called "MyTable" and it basically lists usernames and points (two columns, name and points). I want to say something like "what is joe1928's rank?", which of course is based off his points. How could I do this in MySQL without having to download all that data and sort it and determine the rank myself?
The person with the highest number of points would be ranked 1.
Try getting the number of people with a higher score than your user:
select count(*) from MyTable where score > (select score from MyTable where user = 'Joe');
That will return 0 for the top user.
This page seems to describe and solve your problem.
Notes from that page:
SET #rownum := 0;
SELECT rank, correct FROM (
SELECT #rownum := #rownum + 1 AS rank, correct, uid
FROM quiz_user ORDER BY correct DESC
) as result WHERE uid=xxxxxxxx
SELECT #r AS Rank
FROM MyTable u, (SELECT #r := 0)
WHERE (#r := #r + 1) * (u.Username = 'joe1928')
ORDER BY u.Score DESC
LIMIT 1
select * from [TABLENAME] where [USERNAME] = blah order by [POINTS] desc limit 1;
Based on the link posted by #Dave your query will look like something below:
select Rank,name from
(select #rownum:=#rownum+1 AS 'Rank', p.name
from calls p, (select #rownum:=0) r
order by p.points desc) as rankResults
where name = 'joe';
This is from another stack overflow page, seems to solve your problem.
SELECT uo.*,
(
SELECT COUNT(*)
FROM users ui
WHERE (ui.points, ui.id) >= (uo.points, uo.id)
) AS rank
FROM users uo
WHERE id = #id