левое соединение в рельсах/mysql
-
12-09-2019 - |
Вопрос
Я новичок в рельсах и пытаюсь выполнить левое соединение в 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);
как мне написать это в рельсах как можно элегантнее?
было бы это что-то вроде
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
Спасибо, Павел
Решение
Мне кажется, что вы пытаетесь извлечь из этого запроса две вещи: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
Другие советы
почему бы не использовать :include?
Я думаю, что 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.