更高效在Rails中查找或创建多个记录

Joe*_*oey 5 ruby ruby-on-rails find ruby-on-rails-3

我有一个应用程序需要发送用户事件邀请.当用户邀请朋友(用户)参加活动时,如果尚不存在将用户连接到该事件的新记录,则创建该记录.我的模型由user,event和events_user组成.

class Event
    def invite(user_id, *args)
        user_id.each do |u|
            e = EventsUser.find_or_create_by_event_id_and_user_id(self.id, u)
            e.save!
        end
    end
end
Run Code Online (Sandbox Code Playgroud)

用法

Event.first.invite([1,2,3])
Run Code Online (Sandbox Code Playgroud)

我不认为以上是完成任务的最有效方法.我设想了一种类似的方法

 Model.find_or_create_all_by_event_id_and_user_id
Run Code Online (Sandbox Code Playgroud)

但是一个不存在.

没有验证的模型

class User 
  has_many :events_users 
  has_many :events 
end 
class EventsUser 
  belongs_to :events 
  belongs_to :users 
end 
class Event 
  has_many :events_users 
  has_many :users, :through => :events_users 
end
Run Code Online (Sandbox Code Playgroud)

Rob*_*ert 8

首先获取所有现有记录然后创建所有缺失记录可能会更快:

class Event
  def invite(user_ids, *args)
    existing_user_ids = event_users.where(user_id: user_ids).map(&:user_id)
    (user_ids - existing_user_ids).each do |u|
      event_users.create(user_id: u)
     end
  end
end
Run Code Online (Sandbox Code Playgroud)

这样,如果所有查询都已event_users存在,则只进行1次查询.但是,如果不event_users存在,此方法会执行额外的查询 - 与每次EventUser创建所需的查询数相比.

  • 如果您只对ID列表感兴趣,我将使用#pluck而不是#map,因为pluck只会从数据库中提取单个字段,并且不会为每个结果实例化AR对象。existing_user_ids = event_users.where(user_id:user_ids).pluck(:user_id) (2认同)
  • 如果方法并行运行(线程),则此解决方案容易出错. (2认同)
  • @ dc10你总是可以将它包装在`User.transaction do ... end`中,以确保在你使用它时没有其他线程创建用户. (2认同)
  • 如果您有大量记录,则此解决方案效率不高,因为数组之间存在差异 (2认同)
  • 我认为为了使其更快,不要在每个记录上调用 create ,而是使用 `insert_all` ,如下所示:https://apidock.com/rails/v6.0.0/ActiveRecord/Persistence/ClassMethods/insert_all (2认同)