2

我是 Rails 的新手,并尝试在 mysql 中执行左连接。

有两个对象——用户和消息。

用户 has_and_belongs_to_many 消息,消息 has_and_belongs_to_many 用户

目前,通过简单地编写 user.messages 我在控制台中得到以下查询

SELECT * FROM `messages` INNER JOIN `messages_users` ON `messages`.id = `messages_users`.message_id WHERE (`users_messages`.group_id = 1 )

限制==false 的消息未连接到任何用户,但任何用户都可以访问,我需要将集合 Message.all(restricted=>false) 添加到 user.messages

可以解决我的问题的查询是:

select * from messages left join messages_users on messages_users.message_id=messages.id and messages_users.user_id=1 where (messages_users.user_id is NULL and messages.restricted=false) OR (messages_users.user_id=1 and messages.restricted=true);

我如何尽可能优雅地在rails中编写它?

会不会像

Message.find(:all,:conditions => "(messages_users.user_id is NULL and messages.restricted=false) OR (messages_users.user_id=1 and messages.restricted=true)", :joins => "left join messages_groups on messages_users.message_id=messages.id and messages_users.user_id=1 " )

或者可以更好吗?

我正在使用导轨 2.3.2

谢谢,帕维尔

4

3 回答 3

1

为什么不使用 :include ?

于 2009-08-10T12:04:14.947 回答
1

在我看来,您正在尝试在该查询中提取两件事:1)所有未与受限用户绑定的消息=false 和 2)所有与当前用户绑定的消息受限=true。

如果我理解正确,如果您希望将其作为单个查询完成,我看不到更好的方法。但是,如果您对进行两次查询持开放态度,您可以在代码中稍微清理一下(同时可能会减慢执行速度)。这是替代设置:

class Message < ActiveRecord:Base
  has_and_belongs_to_many :users

  named_scope :restricted, :conditions => {:restricted => true}
  named_scope :unrestricted, :conditions => {:restricted => false}
  named_scope :public, :conditions => "id NOT IN (SELECT DISTINCT message_id FROM messages_users)"
end

class User < ActiveRecord:Base
  has_and_belongs_to_many :messages
end

要以更少的代码获取列表,但需要两次数据库命中,您可以执行以下操作:

@current_user.messages.restricted + Message.unrestricted.public

在任何一种情况下,如果这些表中有大量数据,您需要确保它们被正确索引,否则这会随着任何负载而变慢。如果这是一个发送大量消息的应用程序,则最好使用单个查询。如果它只是一个不会经常使用的辅助功能,我可能会选择更清洁的版本。

从模型的角度来看,可能更好的方法是摆脱 HABTM 关系并明确地对关系建模。然后,您可以方便地跟踪有关消息发送/传递/接收过程的其他数据(例如跟踪发送时间、读取时间等)。它不会改变上面的任何讨论,我只是更喜欢 HABTM。

class Message < ActiveRecord:Base

  has_many :subscriptions
  has_many :users, :through => :subscriptions

  named_scope :restricted,   :conditions => {:restricted => true}
  named_scope :unrestricted, :conditions => {:restricted => false}
  named_scope :public, :conditions => "id NOT IN (SELECT DISTINCT message_id FROM subscriptions)"
end

class User < ActiveRecord:Base

  has_many :subscriptions
  has_many :messages, :through => :subscriptions

end

class Subscription < ActiveRecord:Base

  belongs_to :message
  belongs_to :user

end
于 2009-08-12T19:49:20.290 回答
0

我认为 named_scopes 可能是你的答案。在您的模型中放置如下内容:

named_scope :restricted,   :conditions => {:restricted => true}
named_scope :unrestricted, :conditions => {:restricted => false}

然后你可以调用类似的东西:

Message.restricted
=> All restricted messages

User.first.messages.unrestricted
=> All unrestricted messages belonging to the first user.

我相信这些都是通过 HABTM 协会工作的。

于 2009-08-10T11:18:26.367 回答