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

时间:2011-03-02 00:08:35

标签: ruby-on-rails ruby ruby-on-rails-3 find

我有一个需要发送活动用户邀请的应用。当用户邀请朋友(用户)参加活动时,如果尚不存在将用户连接到该事件的新记录,则创建该记录。我的模型由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

用法

Event.first.invite([1,2,3])

我不认为以上是完成任务的最有效方法。我设想了像

这样的方法
 Model.find_or_create_all_by_event_id_and_user_id

但其中一个不存在。

没有验证的模型

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

3 个答案:

答案 0 :(得分:7)

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

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

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

答案 1 :(得分:1)

最高效的是什么意思?我将假设,通过高效,你的意思是高性能,而不是优雅,干燥,可维护的代码等。

从数据库的角度来看,如果要在数据库中插入100条记录,这将转换为100“INSERT INTO events_models VALUES(x,x)”sql查询(可能是100“SELECT COUNT(*)..”查询是否还有唯一性验证)。因此,即使您想要的方法将在AR中实现,它仍然会在属性数组上有一个循环,并在每个event_id,user_id对上保存。)

从Ruby / Rails的角度来看,如果你想在你的模型上有验证/回调/等,那么你必须在一个循环中逐个创建一个ActiveRecord实例。现在,如果要超级优化方法(删除ActiveRecord类的实例化),可以手动编写sql查询(从而节省一些时间和内存)。然而,与风险相比,收益微乎其微。

顺便说一下, e.save!不是必需的:

  

相同的动态查找器样式可以   用于创建对象,如果它   尚不存在。这种动态   发现者被称为   find_or_create_by_并将返回   对象,如果它已经存在和   否则创建它,然后返回它。

答案 2 :(得分:1)

如果在连接模型中没有任何验证或回调,最快的方法是原始sql:


self.connection.execute(%Q{insert into events_users (event_id,user_id) 
                             select distinct events.id,users.id from events,users 
                             where events.id = #{self.id} 
                             and users.id in ( #{user_ids.join(",")} )
                           }
                        )

在进行上述调用之前,您可以编写一个查询来消除user_ids中的现有记录。我有一个previous question about this,特别是关于没有模型的连接表。

由于您有连接模型,因此可以使用ar-import gem:

books = []
10.times do |i| 
  books << Book.new(:name => "book #{i}")
end
Book.import books