Reputation: 11
I have two tables. The first is Chats table contain messages of current user with friend. And the second table is Threads which save thread chat (A thread chat of user with friend - contain multiple messages)
What I want is: thread chat 1 I want to delete x messages, thread chat 2 delete y messages, thread 3 delete z messages...
How can I achieve it in 1 sql query ?
Column of each table as below:
Table Chats: msgId, threadId, messages, timestamp.
Table Threads: threadId, displayName.
Upvotes: 0
Views: 913
Reputation: 29647
Via a temporary table to store how many records to keep.
And through using a trick to simulate a ROW_NUMBER with PARTITION.
Setup Sample Data:
drop table IF EXISTS TestThreads;
create Table TestThreads (threadId INTEGER NOT NULL primary key, displayName TEXT);
drop table IF EXISTS TestChats;
create Table TestChats (msgId INTEGER NOT NULL primary key AUTOINCREMENT, threadId INTEGER, "timestamp" DATETIME, messages TEXT);
--
-- Sample Data
--
delete from TestThreads;
insert into TestThreads (threadId, displayName) values
(1,'Superman')
,(2,'Son Goku')
,(3,'Bai Xiaochun')
;
delete from TestChats;
insert into TestChats (threadId, "timestamp", messages) values
(1,'2018-01-01 11:15:01','It is not')
,(1,'2018-01-01 11:15:02','an S')
,(1,'2018-01-01 11:15:03','on my world')
,(1,'2018-01-01 11:15:04','it means hope')
,(2,'2018-01-01 12:15:01','Kame')
,(2,'2018-01-01 12:15:02','Hame')
,(2,'2018-01-01 12:15:03','Hhhaaa')
,(2,'2018-01-01 12:15:04','aaaaaaaaaaaaaaaa')
,(3,'2018-01-01 13:15:01','When')
,(3,'2018-01-01 13:15:02','I start deleting')
,(3,'2018-01-01 13:15:03','I frighten')
,(3,'2018-01-01 13:15:04','even myself')
;
Put the threadId's with the number in a temporary table:
--
-- create and fill temporary table
--
DROP TABLE IF EXISTS _tmpThreadsToCleanup;
CREATE TEMP TABLE _tmpThreadsToCleanup(threadId int primary key, NrToDel int);
DELETE FROM _tmpThreadsToCleanup;
INSERT INTO _tmpThreadsToCleanup(threadId, NrToDel)
SELECT threadId, 3 as NrToDel from TestThreads where displayName = 'Superman' UNION ALL
SELECT threadId, 2 as NrToDel from TestThreads where displayName = 'Son Goku' UNION ALL
SELECT threadId, 1 as NrToDel from TestThreads where displayName = 'Bai Xiaochun'
;
The Deletion:
--
-- Delete messages based on the counts in the temporary table
--
DELETE
FROM TestChats
WHERE msgId IN (
SELECT c.msgId
FROM TestChats AS c
JOIN _tmpThreadsToCleanup AS r on (r.threadId = c.threadId)
WHERE (select count(*) from TestChats c2 WHERE c2.threadId = c.threadId AND c2.msgID <= c.msgId) <= r.NrToDel
);
Query what remains:
SELECT th.threadId, th.displayName, ch.msgId, ch."timestamp", ch.messages
FROM TestThreads AS th
LEFT JOIN TestChats AS ch on (ch.threadId = th.threadId)
ORDER BY th.threadId, ch.msgId;
Result:
threadId displayName msgId timestamp messages
-------- ----------- ----- ------------------- ------------
1 Superman 4 2018-01-01 11:15:04 it means hope
2 Son Goku 7 2018-01-01 12:15:03 Hhhaaa
2 Son Goku 8 2018-01-01 12:15:04 aaaaaaaaaaaaaaaa
3 Bai Xiaochun 10 2018-01-01 13:15:02 I start deleting
3 Bai Xiaochun 11 2018-01-01 13:15:03 I frighten
3 Bai Xiaochun 12 2018-01-01 13:15:04 even myself
Remarks:
Because of that join on the count(*) per record to mimic ROW_NUMBER that method should be quite the slowpoke.
But if the numbers of records to delete is the same for the selected threads then LIMIT could be used instead.
DELETE
FROM TestChats
WHERE msgId IN (
SELECT c.msgId
FROM TestChats AS c
JOIN _tmpThreadsToCleanup AS r on (r.threadId = c.threadId)
WHERE c.threadId = TestChats.threadId
ORDER BY c.msgID ASC
LIMIT 2 -- Fixed number
);
If you would want to do the opposite, and keep N records then a LIMIT with an OFFSET can be used for that. The order by should be descending then.
The benefit of that is that such query won't remove extra records when you run it a second time.
DELETE
FROM TestChats
WHERE msgId IN (
SELECT c.msgId
FROM TestChats AS c
JOIN _tmpThreadsToCleanup AS r on (r.threadId = c.threadId)
WHERE c.threadId = TestChats.threadId
ORDER BY c.msgID DESC
LIMIT -1 OFFSET 2 -- Fixed number
);
Upvotes: 1