【问题标题】:SQLite Delete multiple rows with group by and join two tablesSQLite 使用 group by 删除多行并连接两个表
【发布时间】:2018-06-14 01:51:21
【问题描述】:

我有两张桌子。第一个是聊天表,其中包含当前用户与朋友的消息。第二个表是保存线程聊天的线程(用户与朋友的线程聊天 - 包含多条消息)

我想要的是:线程聊天 1 我要删除 x 条消息,线程聊天 2 删除 y 条消息,线程 3 删除 z 条消息...

如何在 1 个 sql 查询中实现它?

每个表格的列如下:

表格聊天:msgId、threadId、消息、时间戳。

表线程:threadId、displayName。

【问题讨论】:

标签: sql sqlite android-sqlite


【解决方案1】:

通过一个临时表来存储要保留多少条记录。
并通过使用一个技巧来模拟带有 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
);

【讨论】:

  • 感谢您提供了很好的解决方案,但是如果未定义线程数,您的解决方案将无法解决问题
  • @AllenWalker 因此,您正在寻找一种解决方案,其中线程显示名称没有硬编码并且要删除固定数量的消息?因为在您的问题中,它建议为每个单独的线程使用不同的数字。
  • 每个线程有不同数量的味精要删除。而且Thread.displayNames不是硬编码的,可以是:Allen, Luk, Batman,..我加了数据例子的截图。
  • @AllenWalker 改变了它。现在使用临时表。顺便说一句,我个人认为保留 N 条记录比删除 N 条记录更安全。
  • 您的解决方案有效,但速度很慢。在我的数据库(大约 50k 行)上大约需要 13 秒
猜你喜欢
  • 1970-01-01
  • 2013-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-24
  • 1970-01-01
  • 2017-06-08
相关资源
最近更新 更多