MySQL: Keep the 10 most recent rows for each user
I have a table saving scores for a game that have 3 columns (more in practice, but it is easier for everyone this way):
userid : the id of the user that got the score
timestamp: the time the score was gotten
score : the score itself
The goal of the query is to delete rows so we have at most 10 scores per user.We also want to keep the most recent rows. So we need to delete the older ones until we get to 10.Note that some players may have less than 10 scores.
How would I manage to do this?
mysql delete
add a comment |
I have a table saving scores for a game that have 3 columns (more in practice, but it is easier for everyone this way):
userid : the id of the user that got the score
timestamp: the time the score was gotten
score : the score itself
The goal of the query is to delete rows so we have at most 10 scores per user.We also want to keep the most recent rows. So we need to delete the older ones until we get to 10.Note that some players may have less than 10 scores.
How would I manage to do this?
mysql delete
I believe approach in answer I got in my question Query: All employee with sum of salary > 500 Can be helpful.
– Grijesh Chauhan
Feb 20 '14 at 11:06
1
What version of MySQL are you using?
– Lennart
Jun 14 '18 at 15:32
add a comment |
I have a table saving scores for a game that have 3 columns (more in practice, but it is easier for everyone this way):
userid : the id of the user that got the score
timestamp: the time the score was gotten
score : the score itself
The goal of the query is to delete rows so we have at most 10 scores per user.We also want to keep the most recent rows. So we need to delete the older ones until we get to 10.Note that some players may have less than 10 scores.
How would I manage to do this?
mysql delete
I have a table saving scores for a game that have 3 columns (more in practice, but it is easier for everyone this way):
userid : the id of the user that got the score
timestamp: the time the score was gotten
score : the score itself
The goal of the query is to delete rows so we have at most 10 scores per user.We also want to keep the most recent rows. So we need to delete the older ones until we get to 10.Note that some players may have less than 10 scores.
How would I manage to do this?
mysql delete
mysql delete
edited Feb 20 '14 at 5:33
Abdul Manaf
6,211105375
6,211105375
asked Feb 20 '14 at 5:16
Maxime MichelMaxime Michel
1612
1612
I believe approach in answer I got in my question Query: All employee with sum of salary > 500 Can be helpful.
– Grijesh Chauhan
Feb 20 '14 at 11:06
1
What version of MySQL are you using?
– Lennart
Jun 14 '18 at 15:32
add a comment |
I believe approach in answer I got in my question Query: All employee with sum of salary > 500 Can be helpful.
– Grijesh Chauhan
Feb 20 '14 at 11:06
1
What version of MySQL are you using?
– Lennart
Jun 14 '18 at 15:32
I believe approach in answer I got in my question Query: All employee with sum of salary > 500 Can be helpful.
– Grijesh Chauhan
Feb 20 '14 at 11:06
I believe approach in answer I got in my question Query: All employee with sum of salary > 500 Can be helpful.
– Grijesh Chauhan
Feb 20 '14 at 11:06
1
1
What version of MySQL are you using?
– Lennart
Jun 14 '18 at 15:32
What version of MySQL are you using?
– Lennart
Jun 14 '18 at 15:32
add a comment |
6 Answers
6
active
oldest
votes
You can try below procedure first test it on local before going to production.
This consider that user has unique timestamp
I have taken that as scored_at
Table Structure
show create table user_scoreG
*************************** 1. row ***************************
Table: user_score
Create Table: CREATE TABLE `user_score` (
`userid` int(11) DEFAULT NULL,
`score` int(11) DEFAULT NULL,
`scored_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
Procedure
DROP PROCEDURE IF EXISTS DeleteRecords;
DELIMITER $$
CREATE PROCEDURE `DeleteRecords`()
BEGIN
DECLARE nomore_userid BOOLEAN DEFAULT FALSE;
DECLARE user_id INT;
DECLARE cursor_delete_users CURSOR FOR
SELECT userid FROM user_score GROUP BY userid HAVING COUNT(userid) > 10;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET nomore_userid = TRUE;
OPEN cursor_delete_users;
REPEAT
FETCH cursor_delete_users INTO user_id;
IF NOT nomore_userid THEN
SELECT COUNT(*) INTO @varCount FROM user_score WHERE userid = user_id;
SET @limit = @varCount - 10;
SET @varSQL = CONCAT('DROP TABLE IF EXISTS tmp_user;');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
SET @varSQL = CONCAT('CREATE TABLE tmp_user SELECT * FROM user_score WHERE userid =',user_id ,' ORDER BY scored_at DESC
LIMIT 10', ',',@limit,';');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
DELETE a.* FROM user_score a JOIN tmp_user b ON a.userid = b.userid and a.scored_at = b.scored_at;
END IF;
UNTIL nomore_userid
END REPEAT;
END$$
DELIMITER ;
add a comment |
I found figured out an answer that works pretty well, though if you have a lot of users it may take some time to execute. I have yet to do any time testing with it. It also assumes you have an id field that uniquely identifies each score.
The way it works is in the inner query it finds up to the 10 most recent scores. Then deletes everything not returned by the inner query.
DELETE FROM score
WHERE id NOT IN (
SELECT id
FROM (
SELECT *
FROM score s1
WHERE (
SELECT COUNT(*)
FROM score s2
WHERE s1.userid = s2.userid
AND s1.timestamp <= s2.timestamp
) <= 10 --Keep this many records
) foo
);
I don't think that the query fits the question.
– ypercubeᵀᴹ
Oct 12 '14 at 1:25
You are right. I edited it to be correct. That's what I get for blindly replacing names. My solution needs a unique id column.
– Lukiahas
Oct 15 '14 at 19:12
add a comment |
Rank the rows by timestamp, and delete any whose rank is greater than 10.
add a comment |
It's difficult to do it in one sentence because MySQL doesn't allows the LIMIT statement in subqueries, that's why we need a temp folder.
We need a temp table to get the oldest timestamp we will maintain
CREATE TABLE i_am_a_temp_folder (
`userid` INT(10) UNSIGNED NOT NULL,
`timestamp` DATETIME NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Getting the oldest timestamp (number 10) of the users which have 10 or more scores
INSERT INTO i_am_a_temp_folder (userid, `timestamp`)
select c1.userid, c1.`timestamp`
from score_table c1
join score_table c2
on c1.userid= c2.useridand c1.`timestamp` <= c2.`timestamp`
group by c1.userid, c1.`timestamp`
having count(*) = 10;
Only on the users with more than 10 scores we will delete the older scores
DELETE c.* FROM score_table c
INNER JOIN i_am_a_temp_folder t
ON c.userid= t.userid
AND c.`timestamp` < t.`timestamp`
Deleting the temp table
drop table i_am_a_temp_folder;
add a comment |
Table structure, example:
row_id int(11) Auto Increment
userid int(11)
score int(11)
updated_at timestamp [CURRENT_TIMESTAMP]
First: Insert a new record
INSERT INTO `user_score` (`userid`, `score`)
VALUES ('50', '80');
Second: Delete all rows start from position 10th, sorted by updated_at descending
DELETE FROM user_score
WHERE row_id < ( SELECT row_id FROM
(SELECT * FROM user_score
ORDER BY updated_at DESC
LIMIT 9,1) AS us)
If we want too keep 10 records, the limit offset is 9
New contributor
add a comment |
Delete from table where id not in ( select top 10 Id from table)
Only if IDs are in ascending order of age.
– Colin 't Hart
Feb 20 '14 at 12:11
5
This answer has several failures. It usesTOP
(which is an SQL-Server thing, not MySQL). It usesTOP
withoutORDER BY
which would result in indeterminate results. It doesn't take into account the timestamp, as specified by the OP. And lastly it would leave only 10 rows in the table while the OP wants to leave 10 rows per user.
– ypercubeᵀᴹ
Feb 20 '14 at 12:16
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "182"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f59238%2fmysql-keep-the-10-most-recent-rows-for-each-user%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can try below procedure first test it on local before going to production.
This consider that user has unique timestamp
I have taken that as scored_at
Table Structure
show create table user_scoreG
*************************** 1. row ***************************
Table: user_score
Create Table: CREATE TABLE `user_score` (
`userid` int(11) DEFAULT NULL,
`score` int(11) DEFAULT NULL,
`scored_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
Procedure
DROP PROCEDURE IF EXISTS DeleteRecords;
DELIMITER $$
CREATE PROCEDURE `DeleteRecords`()
BEGIN
DECLARE nomore_userid BOOLEAN DEFAULT FALSE;
DECLARE user_id INT;
DECLARE cursor_delete_users CURSOR FOR
SELECT userid FROM user_score GROUP BY userid HAVING COUNT(userid) > 10;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET nomore_userid = TRUE;
OPEN cursor_delete_users;
REPEAT
FETCH cursor_delete_users INTO user_id;
IF NOT nomore_userid THEN
SELECT COUNT(*) INTO @varCount FROM user_score WHERE userid = user_id;
SET @limit = @varCount - 10;
SET @varSQL = CONCAT('DROP TABLE IF EXISTS tmp_user;');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
SET @varSQL = CONCAT('CREATE TABLE tmp_user SELECT * FROM user_score WHERE userid =',user_id ,' ORDER BY scored_at DESC
LIMIT 10', ',',@limit,';');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
DELETE a.* FROM user_score a JOIN tmp_user b ON a.userid = b.userid and a.scored_at = b.scored_at;
END IF;
UNTIL nomore_userid
END REPEAT;
END$$
DELIMITER ;
add a comment |
You can try below procedure first test it on local before going to production.
This consider that user has unique timestamp
I have taken that as scored_at
Table Structure
show create table user_scoreG
*************************** 1. row ***************************
Table: user_score
Create Table: CREATE TABLE `user_score` (
`userid` int(11) DEFAULT NULL,
`score` int(11) DEFAULT NULL,
`scored_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
Procedure
DROP PROCEDURE IF EXISTS DeleteRecords;
DELIMITER $$
CREATE PROCEDURE `DeleteRecords`()
BEGIN
DECLARE nomore_userid BOOLEAN DEFAULT FALSE;
DECLARE user_id INT;
DECLARE cursor_delete_users CURSOR FOR
SELECT userid FROM user_score GROUP BY userid HAVING COUNT(userid) > 10;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET nomore_userid = TRUE;
OPEN cursor_delete_users;
REPEAT
FETCH cursor_delete_users INTO user_id;
IF NOT nomore_userid THEN
SELECT COUNT(*) INTO @varCount FROM user_score WHERE userid = user_id;
SET @limit = @varCount - 10;
SET @varSQL = CONCAT('DROP TABLE IF EXISTS tmp_user;');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
SET @varSQL = CONCAT('CREATE TABLE tmp_user SELECT * FROM user_score WHERE userid =',user_id ,' ORDER BY scored_at DESC
LIMIT 10', ',',@limit,';');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
DELETE a.* FROM user_score a JOIN tmp_user b ON a.userid = b.userid and a.scored_at = b.scored_at;
END IF;
UNTIL nomore_userid
END REPEAT;
END$$
DELIMITER ;
add a comment |
You can try below procedure first test it on local before going to production.
This consider that user has unique timestamp
I have taken that as scored_at
Table Structure
show create table user_scoreG
*************************** 1. row ***************************
Table: user_score
Create Table: CREATE TABLE `user_score` (
`userid` int(11) DEFAULT NULL,
`score` int(11) DEFAULT NULL,
`scored_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
Procedure
DROP PROCEDURE IF EXISTS DeleteRecords;
DELIMITER $$
CREATE PROCEDURE `DeleteRecords`()
BEGIN
DECLARE nomore_userid BOOLEAN DEFAULT FALSE;
DECLARE user_id INT;
DECLARE cursor_delete_users CURSOR FOR
SELECT userid FROM user_score GROUP BY userid HAVING COUNT(userid) > 10;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET nomore_userid = TRUE;
OPEN cursor_delete_users;
REPEAT
FETCH cursor_delete_users INTO user_id;
IF NOT nomore_userid THEN
SELECT COUNT(*) INTO @varCount FROM user_score WHERE userid = user_id;
SET @limit = @varCount - 10;
SET @varSQL = CONCAT('DROP TABLE IF EXISTS tmp_user;');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
SET @varSQL = CONCAT('CREATE TABLE tmp_user SELECT * FROM user_score WHERE userid =',user_id ,' ORDER BY scored_at DESC
LIMIT 10', ',',@limit,';');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
DELETE a.* FROM user_score a JOIN tmp_user b ON a.userid = b.userid and a.scored_at = b.scored_at;
END IF;
UNTIL nomore_userid
END REPEAT;
END$$
DELIMITER ;
You can try below procedure first test it on local before going to production.
This consider that user has unique timestamp
I have taken that as scored_at
Table Structure
show create table user_scoreG
*************************** 1. row ***************************
Table: user_score
Create Table: CREATE TABLE `user_score` (
`userid` int(11) DEFAULT NULL,
`score` int(11) DEFAULT NULL,
`scored_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
Procedure
DROP PROCEDURE IF EXISTS DeleteRecords;
DELIMITER $$
CREATE PROCEDURE `DeleteRecords`()
BEGIN
DECLARE nomore_userid BOOLEAN DEFAULT FALSE;
DECLARE user_id INT;
DECLARE cursor_delete_users CURSOR FOR
SELECT userid FROM user_score GROUP BY userid HAVING COUNT(userid) > 10;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET nomore_userid = TRUE;
OPEN cursor_delete_users;
REPEAT
FETCH cursor_delete_users INTO user_id;
IF NOT nomore_userid THEN
SELECT COUNT(*) INTO @varCount FROM user_score WHERE userid = user_id;
SET @limit = @varCount - 10;
SET @varSQL = CONCAT('DROP TABLE IF EXISTS tmp_user;');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
SET @varSQL = CONCAT('CREATE TABLE tmp_user SELECT * FROM user_score WHERE userid =',user_id ,' ORDER BY scored_at DESC
LIMIT 10', ',',@limit,';');
PREPARE statement FROM @varSQL;
EXECUTE statement;
DEALLOCATE PREPARE statement;
DELETE a.* FROM user_score a JOIN tmp_user b ON a.userid = b.userid and a.scored_at = b.scored_at;
END IF;
UNTIL nomore_userid
END REPEAT;
END$$
DELIMITER ;
edited Feb 20 '14 at 6:46
answered Feb 20 '14 at 6:37
Abdul ManafAbdul Manaf
6,211105375
6,211105375
add a comment |
add a comment |
I found figured out an answer that works pretty well, though if you have a lot of users it may take some time to execute. I have yet to do any time testing with it. It also assumes you have an id field that uniquely identifies each score.
The way it works is in the inner query it finds up to the 10 most recent scores. Then deletes everything not returned by the inner query.
DELETE FROM score
WHERE id NOT IN (
SELECT id
FROM (
SELECT *
FROM score s1
WHERE (
SELECT COUNT(*)
FROM score s2
WHERE s1.userid = s2.userid
AND s1.timestamp <= s2.timestamp
) <= 10 --Keep this many records
) foo
);
I don't think that the query fits the question.
– ypercubeᵀᴹ
Oct 12 '14 at 1:25
You are right. I edited it to be correct. That's what I get for blindly replacing names. My solution needs a unique id column.
– Lukiahas
Oct 15 '14 at 19:12
add a comment |
I found figured out an answer that works pretty well, though if you have a lot of users it may take some time to execute. I have yet to do any time testing with it. It also assumes you have an id field that uniquely identifies each score.
The way it works is in the inner query it finds up to the 10 most recent scores. Then deletes everything not returned by the inner query.
DELETE FROM score
WHERE id NOT IN (
SELECT id
FROM (
SELECT *
FROM score s1
WHERE (
SELECT COUNT(*)
FROM score s2
WHERE s1.userid = s2.userid
AND s1.timestamp <= s2.timestamp
) <= 10 --Keep this many records
) foo
);
I don't think that the query fits the question.
– ypercubeᵀᴹ
Oct 12 '14 at 1:25
You are right. I edited it to be correct. That's what I get for blindly replacing names. My solution needs a unique id column.
– Lukiahas
Oct 15 '14 at 19:12
add a comment |
I found figured out an answer that works pretty well, though if you have a lot of users it may take some time to execute. I have yet to do any time testing with it. It also assumes you have an id field that uniquely identifies each score.
The way it works is in the inner query it finds up to the 10 most recent scores. Then deletes everything not returned by the inner query.
DELETE FROM score
WHERE id NOT IN (
SELECT id
FROM (
SELECT *
FROM score s1
WHERE (
SELECT COUNT(*)
FROM score s2
WHERE s1.userid = s2.userid
AND s1.timestamp <= s2.timestamp
) <= 10 --Keep this many records
) foo
);
I found figured out an answer that works pretty well, though if you have a lot of users it may take some time to execute. I have yet to do any time testing with it. It also assumes you have an id field that uniquely identifies each score.
The way it works is in the inner query it finds up to the 10 most recent scores. Then deletes everything not returned by the inner query.
DELETE FROM score
WHERE id NOT IN (
SELECT id
FROM (
SELECT *
FROM score s1
WHERE (
SELECT COUNT(*)
FROM score s2
WHERE s1.userid = s2.userid
AND s1.timestamp <= s2.timestamp
) <= 10 --Keep this many records
) foo
);
edited Oct 15 '14 at 19:11
answered Oct 11 '14 at 21:49
LukiahasLukiahas
113
113
I don't think that the query fits the question.
– ypercubeᵀᴹ
Oct 12 '14 at 1:25
You are right. I edited it to be correct. That's what I get for blindly replacing names. My solution needs a unique id column.
– Lukiahas
Oct 15 '14 at 19:12
add a comment |
I don't think that the query fits the question.
– ypercubeᵀᴹ
Oct 12 '14 at 1:25
You are right. I edited it to be correct. That's what I get for blindly replacing names. My solution needs a unique id column.
– Lukiahas
Oct 15 '14 at 19:12
I don't think that the query fits the question.
– ypercubeᵀᴹ
Oct 12 '14 at 1:25
I don't think that the query fits the question.
– ypercubeᵀᴹ
Oct 12 '14 at 1:25
You are right. I edited it to be correct. That's what I get for blindly replacing names. My solution needs a unique id column.
– Lukiahas
Oct 15 '14 at 19:12
You are right. I edited it to be correct. That's what I get for blindly replacing names. My solution needs a unique id column.
– Lukiahas
Oct 15 '14 at 19:12
add a comment |
Rank the rows by timestamp, and delete any whose rank is greater than 10.
add a comment |
Rank the rows by timestamp, and delete any whose rank is greater than 10.
add a comment |
Rank the rows by timestamp, and delete any whose rank is greater than 10.
Rank the rows by timestamp, and delete any whose rank is greater than 10.
answered Feb 20 '14 at 6:13
James K. LowdenJames K. Lowden
61234
61234
add a comment |
add a comment |
It's difficult to do it in one sentence because MySQL doesn't allows the LIMIT statement in subqueries, that's why we need a temp folder.
We need a temp table to get the oldest timestamp we will maintain
CREATE TABLE i_am_a_temp_folder (
`userid` INT(10) UNSIGNED NOT NULL,
`timestamp` DATETIME NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Getting the oldest timestamp (number 10) of the users which have 10 or more scores
INSERT INTO i_am_a_temp_folder (userid, `timestamp`)
select c1.userid, c1.`timestamp`
from score_table c1
join score_table c2
on c1.userid= c2.useridand c1.`timestamp` <= c2.`timestamp`
group by c1.userid, c1.`timestamp`
having count(*) = 10;
Only on the users with more than 10 scores we will delete the older scores
DELETE c.* FROM score_table c
INNER JOIN i_am_a_temp_folder t
ON c.userid= t.userid
AND c.`timestamp` < t.`timestamp`
Deleting the temp table
drop table i_am_a_temp_folder;
add a comment |
It's difficult to do it in one sentence because MySQL doesn't allows the LIMIT statement in subqueries, that's why we need a temp folder.
We need a temp table to get the oldest timestamp we will maintain
CREATE TABLE i_am_a_temp_folder (
`userid` INT(10) UNSIGNED NOT NULL,
`timestamp` DATETIME NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Getting the oldest timestamp (number 10) of the users which have 10 or more scores
INSERT INTO i_am_a_temp_folder (userid, `timestamp`)
select c1.userid, c1.`timestamp`
from score_table c1
join score_table c2
on c1.userid= c2.useridand c1.`timestamp` <= c2.`timestamp`
group by c1.userid, c1.`timestamp`
having count(*) = 10;
Only on the users with more than 10 scores we will delete the older scores
DELETE c.* FROM score_table c
INNER JOIN i_am_a_temp_folder t
ON c.userid= t.userid
AND c.`timestamp` < t.`timestamp`
Deleting the temp table
drop table i_am_a_temp_folder;
add a comment |
It's difficult to do it in one sentence because MySQL doesn't allows the LIMIT statement in subqueries, that's why we need a temp folder.
We need a temp table to get the oldest timestamp we will maintain
CREATE TABLE i_am_a_temp_folder (
`userid` INT(10) UNSIGNED NOT NULL,
`timestamp` DATETIME NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Getting the oldest timestamp (number 10) of the users which have 10 or more scores
INSERT INTO i_am_a_temp_folder (userid, `timestamp`)
select c1.userid, c1.`timestamp`
from score_table c1
join score_table c2
on c1.userid= c2.useridand c1.`timestamp` <= c2.`timestamp`
group by c1.userid, c1.`timestamp`
having count(*) = 10;
Only on the users with more than 10 scores we will delete the older scores
DELETE c.* FROM score_table c
INNER JOIN i_am_a_temp_folder t
ON c.userid= t.userid
AND c.`timestamp` < t.`timestamp`
Deleting the temp table
drop table i_am_a_temp_folder;
It's difficult to do it in one sentence because MySQL doesn't allows the LIMIT statement in subqueries, that's why we need a temp folder.
We need a temp table to get the oldest timestamp we will maintain
CREATE TABLE i_am_a_temp_folder (
`userid` INT(10) UNSIGNED NOT NULL,
`timestamp` DATETIME NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Getting the oldest timestamp (number 10) of the users which have 10 or more scores
INSERT INTO i_am_a_temp_folder (userid, `timestamp`)
select c1.userid, c1.`timestamp`
from score_table c1
join score_table c2
on c1.userid= c2.useridand c1.`timestamp` <= c2.`timestamp`
group by c1.userid, c1.`timestamp`
having count(*) = 10;
Only on the users with more than 10 scores we will delete the older scores
DELETE c.* FROM score_table c
INNER JOIN i_am_a_temp_folder t
ON c.userid= t.userid
AND c.`timestamp` < t.`timestamp`
Deleting the temp table
drop table i_am_a_temp_folder;
answered Jun 14 '18 at 14:21
NetViciousNetVicious
1012
1012
add a comment |
add a comment |
Table structure, example:
row_id int(11) Auto Increment
userid int(11)
score int(11)
updated_at timestamp [CURRENT_TIMESTAMP]
First: Insert a new record
INSERT INTO `user_score` (`userid`, `score`)
VALUES ('50', '80');
Second: Delete all rows start from position 10th, sorted by updated_at descending
DELETE FROM user_score
WHERE row_id < ( SELECT row_id FROM
(SELECT * FROM user_score
ORDER BY updated_at DESC
LIMIT 9,1) AS us)
If we want too keep 10 records, the limit offset is 9
New contributor
add a comment |
Table structure, example:
row_id int(11) Auto Increment
userid int(11)
score int(11)
updated_at timestamp [CURRENT_TIMESTAMP]
First: Insert a new record
INSERT INTO `user_score` (`userid`, `score`)
VALUES ('50', '80');
Second: Delete all rows start from position 10th, sorted by updated_at descending
DELETE FROM user_score
WHERE row_id < ( SELECT row_id FROM
(SELECT * FROM user_score
ORDER BY updated_at DESC
LIMIT 9,1) AS us)
If we want too keep 10 records, the limit offset is 9
New contributor
add a comment |
Table structure, example:
row_id int(11) Auto Increment
userid int(11)
score int(11)
updated_at timestamp [CURRENT_TIMESTAMP]
First: Insert a new record
INSERT INTO `user_score` (`userid`, `score`)
VALUES ('50', '80');
Second: Delete all rows start from position 10th, sorted by updated_at descending
DELETE FROM user_score
WHERE row_id < ( SELECT row_id FROM
(SELECT * FROM user_score
ORDER BY updated_at DESC
LIMIT 9,1) AS us)
If we want too keep 10 records, the limit offset is 9
New contributor
Table structure, example:
row_id int(11) Auto Increment
userid int(11)
score int(11)
updated_at timestamp [CURRENT_TIMESTAMP]
First: Insert a new record
INSERT INTO `user_score` (`userid`, `score`)
VALUES ('50', '80');
Second: Delete all rows start from position 10th, sorted by updated_at descending
DELETE FROM user_score
WHERE row_id < ( SELECT row_id FROM
(SELECT * FROM user_score
ORDER BY updated_at DESC
LIMIT 9,1) AS us)
If we want too keep 10 records, the limit offset is 9
New contributor
New contributor
answered 5 mins ago
JopieJopie
11
11
New contributor
New contributor
add a comment |
add a comment |
Delete from table where id not in ( select top 10 Id from table)
Only if IDs are in ascending order of age.
– Colin 't Hart
Feb 20 '14 at 12:11
5
This answer has several failures. It usesTOP
(which is an SQL-Server thing, not MySQL). It usesTOP
withoutORDER BY
which would result in indeterminate results. It doesn't take into account the timestamp, as specified by the OP. And lastly it would leave only 10 rows in the table while the OP wants to leave 10 rows per user.
– ypercubeᵀᴹ
Feb 20 '14 at 12:16
add a comment |
Delete from table where id not in ( select top 10 Id from table)
Only if IDs are in ascending order of age.
– Colin 't Hart
Feb 20 '14 at 12:11
5
This answer has several failures. It usesTOP
(which is an SQL-Server thing, not MySQL). It usesTOP
withoutORDER BY
which would result in indeterminate results. It doesn't take into account the timestamp, as specified by the OP. And lastly it would leave only 10 rows in the table while the OP wants to leave 10 rows per user.
– ypercubeᵀᴹ
Feb 20 '14 at 12:16
add a comment |
Delete from table where id not in ( select top 10 Id from table)
Delete from table where id not in ( select top 10 Id from table)
answered Feb 20 '14 at 10:36
user34559user34559
1
1
Only if IDs are in ascending order of age.
– Colin 't Hart
Feb 20 '14 at 12:11
5
This answer has several failures. It usesTOP
(which is an SQL-Server thing, not MySQL). It usesTOP
withoutORDER BY
which would result in indeterminate results. It doesn't take into account the timestamp, as specified by the OP. And lastly it would leave only 10 rows in the table while the OP wants to leave 10 rows per user.
– ypercubeᵀᴹ
Feb 20 '14 at 12:16
add a comment |
Only if IDs are in ascending order of age.
– Colin 't Hart
Feb 20 '14 at 12:11
5
This answer has several failures. It usesTOP
(which is an SQL-Server thing, not MySQL). It usesTOP
withoutORDER BY
which would result in indeterminate results. It doesn't take into account the timestamp, as specified by the OP. And lastly it would leave only 10 rows in the table while the OP wants to leave 10 rows per user.
– ypercubeᵀᴹ
Feb 20 '14 at 12:16
Only if IDs are in ascending order of age.
– Colin 't Hart
Feb 20 '14 at 12:11
Only if IDs are in ascending order of age.
– Colin 't Hart
Feb 20 '14 at 12:11
5
5
This answer has several failures. It uses
TOP
(which is an SQL-Server thing, not MySQL). It uses TOP
without ORDER BY
which would result in indeterminate results. It doesn't take into account the timestamp, as specified by the OP. And lastly it would leave only 10 rows in the table while the OP wants to leave 10 rows per user.– ypercubeᵀᴹ
Feb 20 '14 at 12:16
This answer has several failures. It uses
TOP
(which is an SQL-Server thing, not MySQL). It uses TOP
without ORDER BY
which would result in indeterminate results. It doesn't take into account the timestamp, as specified by the OP. And lastly it would leave only 10 rows in the table while the OP wants to leave 10 rows per user.– ypercubeᵀᴹ
Feb 20 '14 at 12:16
add a comment |
Thanks for contributing an answer to Database Administrators Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f59238%2fmysql-keep-the-10-most-recent-rows-for-each-user%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I believe approach in answer I got in my question Query: All employee with sum of salary > 500 Can be helpful.
– Grijesh Chauhan
Feb 20 '14 at 11:06
1
What version of MySQL are you using?
– Lennart
Jun 14 '18 at 15:32