Select 确切用户之间的对话(按用户 ID)

Select a conversation between exact users (by users IDs)

我正在开发一个简单的聊天应用程序。它应该允许用户 运行 用户对用户或群组(多个用户)的对话。这是我的表格:

table users
ID   | username      | ...

table conversations
ID   | ...

table conversations_users
ID   | user_id       | conversation_id

假设我选择了一些 ID 为 11、22 和 33 的用户,并想检查 这些确切用户(两个、三个或更多)之间的对话是否已经存在在数据库中。我可以通过多个查询和一些后端操作来实现这一点,但我很确定这会对性能产生很大影响。

单次查询是否可行?


附带问题:如果有一个简单的解决方案,它对非常长的表(例如 1.000.000 个对话,conversations_users 中约 3.000.000 行)和多用户查询(让我们检查是否存在100个用户之间的对话)?

一种方法是聚合:

select cu.conversation_id
from conversation_users cu
group by cu.conversation_id
having sum(case when cu.user_id in (11, 22, 33) then 1 else 0 end) = 3;

从性能的角度来看,这样做可能会更快:

select c.*
from conversations c
where exists (select 1
                  from conversation_users cu
                  where cu.conversation_id = c.id and
                        cu.user_id = 11
                 ) and
     exists (select 1
                  from conversation_users cu
                  where cu.conversation_id = c.id and
                        cu.user_id = 22
                 ) and
     exists (select 1
                  from conversation_users cu
                  where cu.conversation_id = c.id and
                        cu.user_id = 33
                 ) and
           not exists (select 1
                  from conversation_users cu
                  where cu.conversation_id = c.id and
                        cu.user_id not in (11, 22, 33)
                 ) ;

这可以利用 conversation_users(user_id) 上的索引。

与任何性能问题一样,您需要对数据库和数据进行测试。无论用户数量如何,第一个查询的性能都非常稳定。第二个会随着用户数量的增加而降低。

当你说:

...whether a conversation between these exact users...

我理解您希望对话中只有这些用户,没有其他人。
在这种情况下只是一个简单的:

sum(case when user_id in (11, 22, 33) then 1 else 0 end) = 3

没有给出正确的结果,因为它会 return 所有 conversation_id 这 3 个用户参与但可能与其他用户一起参与。
您需要与 count(*) 进行比较:

select conversation_id
from conversation_users
group by conversation_id
having sum(user_id in (11, 22, 33)) = count(*);

我相信每个 conversation_id 没有重复的 user_id,所以不需要 count(distinct user_id)
对于这 3 个用户和其他用户之间的对话,您可以使用 where 子句:

select conversation_id
from conversation_users
where user_id in (11, 22, 33)
group by conversation_id
having count(*) = 3;