I have set up a messaging system between characters as follows. Each character has_many conversations through: :chats, and each conversation has_many :messages, some of which belong to one of the participants, and the rest to the other participant.
character.rb
has_many :chats, foreign_key: "character_id",
dependent: :destroy
has_many :conversations, through: :chats, source: :conversation
has_many :messages
conversation.rb
has_many :messages
chat.rb
belongs_to :character
belongs_to :conversation
message.rb
belongs_to :character
belongs_to :conversation
A conversation between two characters "Alf" (character_id: 1) and "Baz" (character_id: 2) would therefore involve two rows in the Chats table with the same conversation_id (10, say):
Chats
character_id conversation_id
1 10
2 10
There might exist another conversation (conversation_id: 23) in which both Alf and Baz are involved with a third user ("Cal", character_id: 7):
Chats
character_id conversation_id
1 23
2 23
7 23
It is important that the query does not select this group conversation.
My question is, how do you construct an SQL query to find the conversation between ONLY Alf and Baz?
I'm stuck because there are three steps, so three SQL queries: first you have to find all conversations that belong to Alf, and then select from these the ones that also belong to Baz, and finally select from these the one that belongs to only Alf and Baz. How do you 'chain' three sql queries in one?
I'm thinking something along these lines:
alf_id = @alf.id
baz_id = @baz.id
find_by_sql(" SELECT *
FROM Chats
RIGHT JOIN Conversations
ON Chats.character_id = #{alf_id}
SELECT *
FROM Conversations
INNER JOIN Chats
ON Chats.conversation_id = Conversations.id
AND Chats.character_id = #{baz_id}
WHERE (conversation belongs to only 2 characters)
; ")
EDIT Possible solution? Can anyone say if this is correct or not?:
sender_id = @sender.id
recipient_id = @recipient.id
conversationID = find_by_sql("
SELECT Conversations.id FROM
(
(
(
Conversations INNER JOIN ( Chats WHERE Chats.character_id=#{sender_id} )
ON Chats.conversation_id=Conversations.id
)
INNER JOIN ( Chats WHERE Chats.character_id = #{recipient_id} )
ON Chats.conversation_id=Conversations.id
)
GROUP BY conversation_id
HAVING COUNT(Chats.conversation_id)=2
)
; ")
Something like this:
select conversation_id
from conversations
group by conversation_id
having
count(case when character_id = <ch1> then 1 end) = 1
and count(case when character_id = <ch2> then 1 end) = 1
and count(*) = 2
Another option is:
select conversation_id from conversations where character_id = <ch1>
intersect
select conversation_id from conversations where character_id = <ch2>
except
select conversation_id from conversations where character_id not in (<ch1>, <ch2>)
The first is probably faster and more portable.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.