2015-11-07 59 views
0

我已經在字符之間建立了一個消息系統,如下所示。每個角色都通過以下方式進行對話:聊天,每個對話has_many:消息,其中一些屬於參與者之一,其餘參與者則屬於其他參與者。Rails sql查詢通過has_many查找屬於兩個不同用戶的記錄通過

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 

兩個字符 「阿爾夫」(character_id:1)之間的對話和 「巴茲」(character_id:2)將因此包括在聊天表具有相同的conversation_id兩排(10,說):

Chats 
character_id conversation_id 
1    10 
2    10 

可能存在另一個會話(conversation_id:23),其中兩個阿爾夫和巴茲都參與與第三用戶(「校準」,character_id:7):

Chats 
character_id conversation_id 
1    23 
2    23 
7    23 

重要的是,查詢不選擇該組會話。

我的問題是,你如何構建一個SQL查詢來找到只有Alf和巴茲之間的對話?

我被卡住了,因爲有三個步驟,所以三個SQL查詢:首先你必須找到所有屬於Alf的對話,然後從這些屬性選擇巴茲,最後從這些選擇一個只屬於Alf和Baz。你如何在一個「連鎖」三個SQL查詢?

我想沿着這些路線的東西:

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) 
      ; ") 

編輯 可能的解決方案? 任何人都可以說,如果這是正確與否?:

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 
    ) 
; ") 

回答

0

事情是這樣的:

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 

另一種選擇是:

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>) 

首先是可能更快,更便於攜帶。