2015-11-07 61 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>) 

首先是可能更快,更便于携带。