通过一个临时表来存储要保留多少条记录。
并通过使用一个技巧来模拟带有 PARTITION 的 ROW_NUMBER。
设置示例数据:
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')
;
将threadId和编号放在一个临时表中:
--
-- 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'
;
删除:
--
-- 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
);
查询剩下的内容:
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;
结果:
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
备注:
因为在每条记录上加入 count(*) 以模仿 ROW_NUMBER,所以该方法应该是相当慢的。
但是,如果要删除的记录数对于所选线程相同,则可以使用 LIMIT 代替。
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
);
如果您想做相反的事情,并保留 N 条记录,那么可以使用带有 OFFSET 的 LIMIT。那么顺序应该是降序的。
这样做的好处是这样的查询在您第二次运行时不会删除额外的记录。
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
);