【问题标题】:Is this possible to do in one query?这可以在一个查询中完成吗?
【发布时间】:2023-03-21 01:10:02
【问题描述】:

我有两个模型:

class User
end

class Message
 belongs_to :sender, :class_name=> 'User'
 belongs_to :recipient, :class_name=> 'User'
end

我想获取给定用户的所有好友,这些好友按给定用户和他的好友之间的对话中出现的消息的最近日期排序,并且如果可能在同一个查询中,获取他们对话中的消息数。

现在,我陷入了困境:

Messages.all(:joins => :sender,
   :conditions => ['sender_id = ? OR recipient_id = ?', some_user.id, some_user.id],
   :select => 'users.*, sender_id, recipient_id, MAX(messages.created_at) as last_created, COUNT(messages.id) as messages_count', 
   :group => 'messages.sender_id, messages.recipient_id',
   :order => 'last_created DESC'

该查询产生以下输出:

一)

users.* | sender_id | recipient_id | MAX(last_created) | messages_count
user1   | 1         | 2            | bla               | bla
user1   | 1         | 3            | bla               | bla
user1   | 1         | 4            | bla               | bla

因为messages.sender_id = user.id 加入的模型我只获取了 user1 记录,但我需要 user2user3user4 em> 记录那个特殊情况 A 当 user1 只向他的好友发送消息时。

b)

users.* | sender_id | recipient_id | MAX(last_created) | messages_count
user2   | 2         | 1            | bla               | bla
user3   | 3         | 1            | bla               | bla
user4   | 4         | 1            | bla               | bla

在情况 B 中,否则,我有我想要的 - 所有三个好友都按消息的最近日期排序,出现在给定用户和他的好友之间的对话中。

c)

users.* | sender_id | recipient_id | MAX(last_created) | messages_count
user1   | 1         | 2            | bla               | bla
user3   | 3         | 1            | bla               | bla
user4   | 4         | 1            | bla               | bla

情况 C. 作为 user1 的好友的 user2 缺少原因 :joins => :sender。否则,如果:joins => :recipient 将缺少 user3 和 user4。那是饼干。 无论我们如何加入模型。如何在一个查询中解决这种情况?

【问题讨论】:

  • 只有一件事:它是:class_name

标签: sql ruby-on-rails activerecord


【解决方案1】:

您需要 select_extra_columns gem 来返回连接/聚合列。假设您已经安装了 gem,如下所示修改您的 User 模型。

class User
  select_extra_columns


  def friends_with_conversation
    User.all(
     :select => "users.*, b.last_message_at, b.message_count",
     :joins  => "
            RIGHT JOIN
             ( SELECT   IF(a.sender_id=#{self.id}, a.recipient_id, 
                             a.sender_id) AS friend_id, 
                        MAX(a.created_at) AS last_message_at, 
                        COUNT(a.id)       AS message_count
               FROM     messages AS a
               WHERE    a.sender_id = #{self.id} OR 
                        a.recipient_id = #{self.id}
               GROUP BY IF(a.sender_id=#{self.id}, a.recipient_id, 
                             a.sender_id)
             ) AS b ON users.id = b.friend_id
           ", 
      :order  => "b.last_message_at DESC",      
      :extra_columns => {:last_message_at=>:datetime, :message_count => :integer}
    )
  end  
end

现在您可以拨打以下电话以获取朋友的详细信息。

user.friends_with_conversation.each do |friend|
  p friend.name
  p friend.last_message_at
  p friend.message_count
end

您需要 gem 在查询返回的 User 对象中返回 last_message_atmessage_count

编辑 我不熟悉 PostgresSQL。粗略阅读文档表明,遵循 SQL 可能有效。

:joins  => "
 RIGHT JOIN
 ( SELECT   CASE WHEN a.sender_id=#{self.id} 
                 THEN a.recipient_id 
                 ELSE a.sender_id 
            END               AS friend_id, 
            MAX(a.created_at) AS last_message_at, 
            COUNT(a.id)       AS message_count
   FROM     messages AS a
   WHERE    a.sender_id = #{self.id} OR 
            a.recipient_id = #{self.id}
   GROUP BY CASE WHEN a.sender_id=#{self.id} 
                 THEN a.recipient_id 
                 ELSE a.sender_id 
            END
 ) AS b ON users.id = b.friend_id
"

【讨论】:

  • 谢谢,我认为它实际上可以工作,但我在 PostgreSQL 中开发,似乎无法理解这种语法。您介意为 PostgreSQL 添加此查询的版本吗?再次感谢。
  • 我已将 PostgresSQL 添加到我的答案中
  • 像魅力一样工作。谢谢你。非常复杂的答案,非常有帮助。我发现,所有作品都没有 select_extra_columns gem。为什么我仍然需要它?
  • 如果你想在同一个查询中得到last_message_atmessage_count,那么你需要gem。没有 gem,friend.message_count 之类的调用将无法工作。
【解决方案2】:

看起来你想要一个替代的“加入”字符串(即不是:joins => :sender)。 :joins => :recipient 会针对情况 B 给出正确的响应吗?

如果没有 - 您也可以将手工制作的 SQL 传递给 :joins 键,然后按照您喜欢的方式加入表。

看起来这里有一个很好的关于连接的教程: http://www.railway.at/articles/2008/04/24/database-agnostic-database-ignorant/

【讨论】:

  • :joins => :recipient 为 B 中的所有记录提供 user1。所以,这是不正确的。整个技巧:)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-13
  • 1970-01-01
相关资源
最近更新 更多