Upon submitting a review, I'm trying to run a query that deletes all rows with matching account number, except the eleven most recent. My table consists of "account","stamp"(datetime), and "reviews".
The account number is passed in the variable $account.
DELETE FROM table
WHERE account=?
AND stamp NOT IN (SELECT stamp FROM table ORDER BY stamp DESC LIMIT 11)
Not sure what is wrong with the query.
You can do this with a delete and join:
delete t
from table t left join
(select t.*
from table t
where account = ?
order by stamp desc
limit 11
) tt
on t.account = tt.account and t.stamp = tt.stamp
where t.account = ? and tt.account is null;
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
I'm running a query in MySQL with an INNER JOIN that has a LIMIT on the subquery
The problem is, that the LIMIT on the subquery is affecting the number of rows returned.
I want to select all rows from table 1 (tickets) where the last row in ticket_updates relevant (t.ticketnumber = tu.ticketnumber) was not numeric in column contact_name
SELECT t.*
FROM tickets t
JOIN
( SELECT ticketnumber
FROM ticket_updates
WHERE type = 'update'
AND concat('', contact_name * 1) <> contact_name
ORDER
BY sequence DESC
LIMIT 1
) tu
ON t.ticketnumber = tu.ticketnumber
WHERE t.status <> 'Completed'
AND LOWER(t.department) = 'support';
But the results shown just return the 1 row
There are multiple rows in ticket_updates that relate to each row in tickets based on tickets.ticketnumber =ticket_updates.ticketnumber`
the contact_name column can either be a string or integer. I picked up the concat('', contact_name * 1) <> contact_name from another SO Post which tells me whether the value is numeric or not.
So I want to pick up the latest row (ORDER BY sequence DESC) in ticket_updates for each row in tickets and see whether contact_name is not numeric
Your query selects one row because (as you concluded yourself) your subquery is limited to one result.
what you want is probably something similar to what can be found in this answer (I assumed that you want the entry with biggest value for sequence, if the contratry, change to MIN)
with a subquery (there should be an adaptation from the shortest more optimized query in the cited answer, but let's see if this one works first):
SELECT t.*
FROM tickets t
INNER JOIN ( SELECT ticketnumber, MAX(tu.sequence) AS maxSequence
FROM ticket_updates tu
WHERE tu.type = 'update' AND concat('', tu.contact_name * 1) <> tu.contact_name
GROUP BY ticketnumber ) tu2
ON (t.ticketnumber = tu2.ticketnumber)
WHERE t.status <> 'Completed'
AND LOWER(t.department) = 'support';
see it in action
I have a MySQL table targeted_refills with columns: id (unique, primary key), pat_name, pat_phone, rx_number, drug_name, qty_disp, last_date, qty_left, price_code, last_price, last_contact, doc_name, ehf, userid
What I'm trying to do is list one record for each userid. The record I want listed is the most recent one based on the field last_date however if there is more than one record for that user with the most recent date then I want it to select the field with the largest value in the field last_price. In the rare event that there are two records with matching last_date and last_price (shouldn't happen but is possible), I really don't care which is chosen so it can be chosen off the largest value of id since that is unique and the primary key.
EDIT:
I've tried this query that I pieced together from another person's question:
SELECT t1.*
FROM `targeted_refills` AS t1
LEFT OUTER JOIN `targeted_refills` AS t2
ON t1.userid = t2.userid
AND (t1.last_date < t2.last_date
OR (t1.last_date = t2.last_date AND t1.last_price < t2.last_price))
WHERE t2.userid IS NULL;
and it does a reasonable job of getting where I want to go, but it's pulling a few more records than I want. I'm comparing now to see what extra records it's pulling and why. It also takes a long time to run.
UPDATED You can tweak your existing query a little bit: you need at least one more join condition that prevent duplicates when both last_date and last_price are equal across several rows. Since you have auto-generated id you can use it to differentiate rows.
SELECT t1.*
FROM targeted_refills t1 LEFT JOIN targeted_refills t2
ON t1.userid = t2.userid
AND (
t1.last_date < t2.last_date
OR
(t1.last_date = t2.last_date AND
t1.last_price < t2.last_price)
OR
(t1.last_date = t2.last_date AND
t1.last_price = t2.last_price AND
t1.id < t2.id)
)
WHERE t2.userid IS NULL;
Or you can achieve your goal another way
SELECT id, pat_name, pat_phone, rx_number, drug_name,
qty_disp, last_date, qty_left, price_code,
last_price, last_contact, doc_name, ehf, userid
FROM
(
SELECT r.*,
#n := IF(#u = userid, #n + 1, 1) rownum,
#u := userid
FROM targeted_refills r
ORDER BY userid, last_date DESC, last_price DESC
) q
WHERE q.rownum = 1
Here is SQLFiddle demo for both queries.
I have the following MySQL statement
SELECT * FROM user_messages AS T WHERE user_id = '33' AND id = (SELECT Max(id) from user_messages AS TT WHERE T.from_userid = TT.from_userid) ORDER BY status, id DESC
The problem I seem to be having is when I only have one record. I would think that MySQL would return the single record associated with user_link = '33', but instead it returns nothing.
I need to use the "Max" function because I use it to pull the most recent entries. I am trying to avoid having multiple queries or having to use php to sort also. Any help much appreciated!
This is your query:
SELECT *
FROM user_messages AS T
WHERE user_id = '33' AND
id = (SELECT Max(id)
from user_messages AS TT
WHERE T.from_userid = TT.from_userid
)
ORDER BY status, id DESC
Here are three reasons it could be failing to return any rows. First, user_id = '33' may not exist in the table. Second from_userid may be NULL. Third, the id value may be NULL for all matching records.
Perhaps this simpler version would help:
select *
from user_messages um
where user_id = '33'
order by id desc
limit 1
Thanks for your answer Gordon, I checked the database, and the record exists. I did some more research, and what it turns out to be is that I needed to join the data. I was able to return the Min or Max record, but the corresponding/related fields weren't returned with it.
SELECT * FROM user_messages INNER JOIN(SELECT from_username, MAX(id) AS id FROM user_messages WHERE user_link = '33' GROUP BY from_username ORDER BY status, id DESC) t2 ON user_messages.id = t2.id AND user_messages.from_username = t2.from_username
The thread that answered the question was this one - Need To Pull Most Recent Record By Timestamp Per Unique ID
Please can someone help me, I have a table of users and another table with users and date time (this is a log file and multiple dates exist per user). I need to take the most recent date from the log table and insert it into the first table next to the same user.
This is what I have but its not working:
INSERT INTO tb1 n (DT)
SELECT w.DT
FROM tb2 w
WHERE w.User = n.User
ORDER BY w.DT DESC
limit 1
you don't need to use INSERT statement here since there are already records present on your table. But instead UPDATE it with JOIN
UPDATE tb1 a
INNER JOIN
(
SELECT user, MAX(DT) maxDT
FROM tb2
GROUP by user
) b ON a.user = b.user
SET a.DT = b.maxDT