Reputation: 5441
I have the following database schema for a simple chat app (simplified):
## users table
id
## chats table
id, type
## chat_user
chat_id, user_id
The issue I want to solve is that given I have 2 users, how can I get a chat
record for a given type
given I know that this specific chat type
should only exist once if it already exists?
For example, we have bob (id: 1) and james (id: 2). bob initiates a chat with james:
insert into chats (type) values ('private') returning id;
-- returned ID is: 1234
insert into chat_user (chat_id, user_id) values (1234, 1);
insert into chat_user (chat_id, user_id) values (1234, 2);
Now I want a query that will yield the chat
row with the ID 1234
if I give it the 2 users.
one solution I found for that is finding the intersecting chat_id
for those 2 users and since I know that there is only ever 1 private
chat for those 2 users, I simply take the first result:
select * from chats where id in (
select chat_id from chat_user where user_id = 1 and chat_id in (
select chat_id from chat_user where user_id = 2
)
) and type = 'private' limit 1;
Now, while this works, I wonder if this is the best solution for this? Will this query get slower and slower as users have more chats?
If it matters, I'm using Postgres for this chat app.
Upvotes: 2
Views: 143
Reputation: 164139
You can use INTERSECT
:
select * from chats
where type = 'private'
and id in (
select chat_id from chat_user where user_id = 1
intersect
select chat_id from chat_user where user_id = 2
)
limit 1;
I don't think that LIMIT 1 is needed if there can only be at most 1 result returned.
Or join the tables, filter, group by id and set a condition in the having
clause:
select c.id
from chats c inner join chat_user u
on u.chat_id = c.id
where c.type = 'private' and u.user_id in (1, 2)
group by c.id
having count(*) = 2
Upvotes: 2
Reputation: 1270401
Can more than two users be in a private chat? If so, aggregation with having
is probably the best approach:
select c.id
from chats c inner join
chat_user cu
on cu.chat_id = c.id
where c.type = 'private'
group by c.id
having count(*) filter (where cu.user_id in (1, 2)) = 2;
Of course, this works for only two users as well. It just handles the more general case as well.
Upvotes: 0