Rails sql查询通过has_many到

时间:2015-11-07 18:30:53

标签: sql ruby-on-rails activerecord

我在字符之间设置了一个消息系统,如下所示。每个角色通过:: chats和每个对话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

两个角色之间的对话" Alf" (character_id:1)和" Baz"因此,(character_id:2)将在Chats表中包含两行,并且具有相同的conversation_id(10,比方说):

Chats
character_id    conversation_id
1               10
2               10

可能存在另一个对话(conversation_id:23),其中Alf和Baz都与第三个用户有关(" Cal",character_id:7):

Chats
character_id    conversation_id
1               23
2               23
7               23

查询不选择此群组对话非常重要。

我的问题是,你如何构建一个SQL查询来查找只有Alf和Baz之间的对话?

我被困了,因为有三个步骤,所以三个SQL查询:首先你必须找到属于Alf的所有对话,然后从这些对象中选择也属于Baz,最后从这些中选择那个只属于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)
           ; ")

EDIT 可能的方案? 任何人都可以说这是否正确?:

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

1 个答案:

答案 0 :(得分: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>)

第一种可能更快,更便携。