I address this question back on Jun 08, 2012 : Update rank on a large table
First, make sure you have an index on rank
and last_rank
in the rank
table
Next, make sure you have a points
index on the score table
If you do not have such indexes, run the following:
ALTER TABLE rank ADD INDEX (rank);
ALTER TABLE rank ADD INDEX (last_rank);
ALTER TABLE score ADD INDEX (points);
You are going to have to update the last_rank on the fly using an UPDATE...JOIN
:
SET @r=0;
UPDATE (SELECT id score_id FROM `score` ORDER BY points DESC) A
LEFT JOIN `rank` B USING (score_id)
SET B.last_rank = B.rank,B.rank = @r:= (@r+1)
;
Here is a more paranoid version
DROP TABLE IF EXISTS score_rank;
CREATE TABLE score_rank
(
id INT NOT NULL AUTO_INCREMENT,
score_id INT NOT NULL,
rank INT,
last_rank INT,
PRIMARY KEY (id),
);
SET @r=0;
INSERT INTO score_rank (last_rank,rank,score_id)
SELECT B.rank,@r:= (@r+1),B.score_id FROM
(SELECT id score_id FROM `score` ORDER BY points DESC) A
LEFT JOIN `rank` B USING (score_id)
;
UPDATE rank A INNER JOIN score_rank B SET
A.rank = B.rank,
A.last_rank = B.last_rank
;
DROP TABLE IF EXISTS score_rank;
Give it a Try !!!
CAVEAT
If the table is very large, it may take time. As I suggested in Update rank on a large table, you may want to limit the ranking to the first 10,000 when ranking frequently, and then do the fuill ranking maybe once a week or once a month.
Therefore, ranking top 10,000 should look this
SET @r=0;
UPDATE (SELECT id score_id FROM `score` ORDER BY points DESC LIMIT 10000) A
LEFT JOIN `rank` B USING (score_id)
SET B.last_rank = B.rank,B.rank = @r:= (@r+1)
;
or this
DROP TABLE IF EXISTS score_rank;
CREATE TABLE score_rank
(
id INT NOT NULL AUTO_INCREMENT,
score_id INT NOT NULL,
rank INT,
last_rank INT,
PRIMARY KEY (id),
);
SET @r=0;
INSERT INTO score_rank (last_rank,rank,score_id)
SELECT B.rank,@r:= (@r+1),B.score_id FROM
(SELECT id score_id FROM `score` ORDER BY points DESC LIMIT 10000) A
LEFT JOIN `rank` B USING (score_id)
;
UPDATE rank A INNER JOIN score_rank B SET
A.rank = B.rank,
A.last_rank = B.last_rank
;
DROP TABLE IF EXISTS score_rank;
Best Answer
While keeping your approach to the solution, the query will be