I have a table of 10 high scores, when a new user comes along with a new high score that's greater than the lowest, I want to delete the minimum score from the table. I have tried every suggested way of doing this. Please can someone help.
Something like this should work. You'd run it after inserting the new record.
delete from t t1 where t1.score not in
(select t2.score from t t2 order by t2.score desc limit 10)
If you have a unique ID for each record in the table, then I'd modify it as follows:
delete from t t1 where t1.id not in
(select t2.id from t t2 order by t2.score desc limit 10)
Don't delete from the table -- way too much maintenance involved. Just run the query:
select t.*
from t
order by t.score desc
limit 10;
You can put an index on (score), so this runs faster.
You can encapsulate this in a view.
Related
I have a big table of messages with date and room columns. and 2 billion rows.
now i want keep only last 50 messages for every room and delete previous messages.
can i do it with a fast query ?
this question is unique , i didn't found any other question for delete rows over a grouped and ordered selection
You cannot do it in a fast query. You have a lot of data.
I would suggest creating a new table. You can then replace the data in your first table, if necessary.
Possibly the most efficient method to get the 50 rows -- assuming that date is unique for each room:
select t.*
from t
where t.date >= coalesce((select t2.date
from t t2
where t2.room = t.room
order by t2.date desc
limit 1
), t.date
);
For this to have any hope of performance you want an index on (room, date).
You can also try row_number() in MySQL 8+:
select . . . -- list the columns
from (select t.*, row_number() over (partition by room order by date desc) as seqnum
from t
) t
where seqnum <= 50;
Then you can replace the data by doing:
create table temp_t as
select . . . -- one of the select queries here;
truncate table t; -- this gets rid of all the data, so be careful
insert into t
select *
from temp_t;
Massive inserts are much more efficient than massive updates, because the old data does not need to be logged (nor the pages locked and other things).
You can use Rank() function to get top 50 results for each group ordered by date desc, so the last entries will be in top.
http://www.mysqltutorial.org/mysql-window-functions/mysql-rank-function/
Then you left join that subquery on your table on id ( or room and date, if those are unique and you don’t have id in your table)
The last step would be to filter all such result that have null in subquery and delete those.
The full code will look something like this:
DELETE T FROM YOURTABLE T
LEFT JOIN (
SELECT *,
RANK() OVER (PARTITION BY
ROOM
ORDER BY
[DATE] DESC
) DATE_RANK
) AS T2
ON T.[DATE] = T2.[DATE]
AND T.ROOM = T2.ROOM
AND T2.DATE_RANK<=50
WHERE T2.DATE IS NULL
my problem is that I want this:
SELECT * FROM table
WHERE userId = 7243
ORDER BY date desc LIMIT 1
But for multiple ids in one request.
I tried this:
SELECT * FROM table
WHERE userId IN (7243, 1)
GROUP BY userId
ORDER BY date desc
But the order by seems to be ignored. Do anyone has a solution for me? Thank you
If you want the max date record for each of the two IDs, then you may use a subquery:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT userId, MAX(date) AS max_date
FROM yourTable
WHERE userId IN (7243, 1)
GROUP BY userId
) t2
ON t1.userId = t2.userId AND t1.date = t2.max_date
WHERE
t1.userId IN (7243, 1);
This is the just greatest-value-per-group question with a slight twist, namely that you only want to see two of the possible groups in the output.
As #Raymond commented below, an index on (userId, date) should greatly speed up the t2 subquery. I am not sure if this index would help beyond that, but it should make a difference.
I recently came across a query in one of our office discussions,
SELECT t1.id, t1.name, t1.date AS date_filter,
(SELECT t2.column_x
FROM table_2 t2
WHERE t2.date = date_filter LIMIT 1
) AS column_x
FROM table_1 t1
WHERE t1.category_id = 10
ORDER BY t1.date
LIMIT 10;
The sub-query returns a column value from a second table that matches the date from the first table.
This query is not running at an optimised speed, can you guys pass me what are the ways to improvise the performance ?
Cheers
It would help to have SHOW CREATE TABLE for both tables, plus EXPLAIN SELECT ...
Indexes needed:
t1: INDEX(category_id, date)
t2: INDEX(date)
The subquery does not make sense without an ORDER BY -- which "1" row do you want?
I am trying to concoct a mysql query that returns, in a single row, the fields for a given row, as well as a few fields from a row that matches the "previous" position, and the same fields for the "next" position. I'm pretty new at mysql, but for all the scouring the net for answers, this is the best I can do:
SELECT *,
(select id
from mytable t2
where t2.added_on < t.added_on
order by t2.added_on DESC
limit 1
) as prev_id,
(select id
from mytable t3
where t3.added_on > t.added_on
order by t3.added_on
limit 1
) as next_id FROM mytable as t ORDER BY `added_on`
which works, but only gives me the id field for the "previous" and "next". As you may know, using * (or 'id', 'title') instead of id in the subqueries gives an error. I've looked into using JOINs and some other approaches but I'm just simply not getting it.
OK, I figured it out, here is the solution for all of you out there trying to find a solution to this. Make sure to vote up if it helped you.
SELECT t.*,
prev.id as prev_id,
prev.added_on as prev_added_on,
next.id as next_id,
next.added_on as next_added_on
FROM `TABLE_NAME` AS t
LEFT JOIN `TABLE_NAME` AS prev
ON prev.id =
(select id
from `TABLE_NAME` t2
where t2.added_on < t.added_on
order by t2.added_on DESC
limit 1)
LEFT JOIN `TABLE_NAME` AS next
ON next.id =
(select id
from `TABLE_NAME` t3
where t3.added_on > t.added_on
order by t3.added_on
limit 1 )
ORDER BY t.added_on DESC
That last ORDER BY actually causes the whole thing to be greatly optimized for some reason (my current understanding of mysql doesn't let me know why this is for sure) and in my case it makes execution at least twice as fast as without it.
Is it possible to use if/then statements in mySql? I am looking to check the value of a column in a JOIN table and order the results based on the value of that column.
My sorry attempt:
SELECT t1.*
FROM t1
JOIN t2 ON t1.ID = t2.ID
WHERE t1.ID = '888'
if(t2.col1 = '1') ORDER BY t1.tid ASC
else ORDER BY RAND()
You can use CASE:
ORDER BY
CASE WHEN t2.col1 = 1 THEN t1.tid ELSE rand() END ASC
Beware the performance of this may not be so good, as MySQL won't be able to use an index for the order t1.tid.
You should also be aware that you can order by multiple things, so that possibly this will do what you want:
ORDER BY t1.tid ASC, RAND()
Also, you should keep in mind that ORDER BY RAND() LIMIT 1 will actually fetch every row, calculate a random number for each, sort them, then finally just return the first row. So on data of a reasonable size, it will be slow (and also result in temp tables).