简体   繁体   中英

SQLite Delete multiple rows with group by and join two tables

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.

线程表的数据示例

聊天表的数据示例

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
);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM