SQLite使用group by删除多个行并连接两个表

时间:2018-06-12 11:32:57

标签: sql sqlite android-sqlite

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

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

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

每个表的列如下:

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

表线程:threadId,displayName。

Data example for Thread table

Data example for Chat table

1 个答案:

答案 0 :(得分:0)

通过临时表存储要保留的记录数 并通过使用技巧来模拟带有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

<强>说明:

由于每个记录的计数(*)的连接模仿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
);