在rails中的单个调用中保存多个对象。

时间:2022-09-22 18:09:03

I have a method in rails that is doing something like this:

我在rails中有一个方法就是这样做:

a = Foo.new("bar")
a.save

b = Foo.new("baz")
b.save

...
x = Foo.new("123", :parent_id => a.id)
x.save

...
z = Foo.new("zxy", :parent_id => b.id)
z.save

The problem is this takes longer and longer the more entities I add. I suspect this is because it has to hit the database for every record. Since they are nested, I know I can't save the children before the parents are saved, but I would like to save all of the parents at once, and then all of the children. It would be nice to do something like:

问题是,我添加的实体越多,这需要的时间就越长。由于它们是嵌套的,我知道我不能在父母得救之前拯救孩子,但我想同时拯救所有的父母,然后拯救所有的孩子。如果能做一些像:

a = Foo.new("bar")
b = Foo.new("baz")
...
saveall(a,b,...)

x = Foo.new("123", :parent_id => a.id)
...
z = Foo.new("zxy", :parent_id => b.id)
saveall(x,...,z)

That would do it all in only two database hits. Is there an easy way to do this in rails, or am I stuck doing it one at a time?

这只需要两个数据库就可以完成。是否有一种简单的方法在rails中实现这一点,或者我一次只能做一个?

4 个解决方案

#1


52  

You might try using Foo.create instead of Foo.new. Create "Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not."

你可以试试用Foo。创建Foo.new的相反。创建“创建一个对象(或多个对象)并将其保存到数据库,如果验证通过。无论对象是否被成功保存到数据库中,都会返回结果对象。

You can create multiple objects like this:

您可以创建多个对象,如下所示:

# Create an Array of new objects
  parents = Foo.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])

Then, for each parent, you can also use create to add to its association:

然后,对于每个父类,您还可以使用create将其关联添加到:

parents.each do |parent|
  parent.children.create (:child_name => 'abc')
end

I recommend reading both the ActiveRecord documentation and the Rails Guides on ActiveRecord query interface and ActiveRecord associations. The latter contains a guide of all the methods a class gains when you declare an association.

我建议您阅读ActiveRecord文档和ActiveRecord查询接口和ActiveRecord关联的Rails指南。后者包含类在声明关联时获得的所有方法的指南。

#2


67  

Since you need to perform multiple inserts, database will be hit multiple times. The delay in your case is because each save is done in different DB transactions. You can reduce the latency by enclosing all your operations in one transaction.

由于需要执行多个插入,因此数据库将被多次命中。在这种情况下,延迟是因为每次保存都是在不同的DB事务中完成的。您可以通过将所有操作封装在一个事务中来减少延迟。

class Foo
  belongs_to  :parent,   :class_name => "Foo"
  has_many    :children, :class_name => "Foo", :foreign_key=> "parent_id"
end

Your save method might look like this:

您的save方法可能是这样的:

# build the parent and the children
a = Foo.new(:name => "bar")
a.children.build(:name => "123")

b = Foo.new("baz")
b.children.build(:name => "zxy")

#save parents and their children in one transaction
Foo.transaction do
  a.save
  b.save
end

The save call on the parent object saves the child objects.

父对象上的save调用保存子对象。

#3


8  

One of the two answers found somewhere else: by Beerlington. Those two are your best bet for performance

在其他地方找到的两个答案之一:伯灵顿(Beerlington)著。这两个是你的最佳选择


I think your best bet performance-wise is going to be to use SQL, and bulk insert multiple rows per query. If you can build an INSERT statement that does something like:

我认为,最好的性能选择是使用SQL,每次查询大量插入多行。如果您可以构建一个INSERT语句,其功能如下:

INSERT INTO foos_bars (foo_id,bar_id) VALUES (1,1),(1,2),(1,3).... You should be able to insert thousands of rows in a single query. I didn't try your mass_habtm method, but it seems like you could to something like:

插入foos_bars(foo_id bar_id)值(1,1),(1、2),(1、3)....您应该能够在一个查询中插入数千行。我没有尝试过你的mass_habtm方法,但是看起来你可以这样做:


bars = Bar.find_all_by_some_attribute(:a) 
foo = Foo.create
values = bars.map {|bar| "(#{foo.id},#{bar.id})"}.join(",") 
connection.execute("INSERT INTO foos_bars (foo_id, bar_id) VALUES
#{values}")

Also, if you are searching Bar by "some_attribute", make sure you have that field indexed in your database.

此外,如果您正在使用“some_attribute”搜索Bar,请确保在数据库中对该字段进行了索引。


OR

You still might have a look at activerecord-import. It's right that it doesn't work without a model, but you could create a Model just for the import.

您仍然可以查看activerecord-import。没有模型是不行的,但是您可以为导入创建一个模型。


FooBar.import [:foo_id, :bar_id], [[1,2], [1,3]]

Cheers

干杯

#4


0  

You don't need a gem to hit DB fast and only once!

你不需要宝石就能快速命中DB,而且只有一次!

Jackrg has worked it out for us: https://gist.github.com/jackrg/76ade1724bd816292e4e

Jackrg为我们解决了这个问题:https://gist.github.com/jackrg/76ade1724bd816292e4e

#1


52  

You might try using Foo.create instead of Foo.new. Create "Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not."

你可以试试用Foo。创建Foo.new的相反。创建“创建一个对象(或多个对象)并将其保存到数据库,如果验证通过。无论对象是否被成功保存到数据库中,都会返回结果对象。

You can create multiple objects like this:

您可以创建多个对象,如下所示:

# Create an Array of new objects
  parents = Foo.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])

Then, for each parent, you can also use create to add to its association:

然后,对于每个父类,您还可以使用create将其关联添加到:

parents.each do |parent|
  parent.children.create (:child_name => 'abc')
end

I recommend reading both the ActiveRecord documentation and the Rails Guides on ActiveRecord query interface and ActiveRecord associations. The latter contains a guide of all the methods a class gains when you declare an association.

我建议您阅读ActiveRecord文档和ActiveRecord查询接口和ActiveRecord关联的Rails指南。后者包含类在声明关联时获得的所有方法的指南。

#2


67  

Since you need to perform multiple inserts, database will be hit multiple times. The delay in your case is because each save is done in different DB transactions. You can reduce the latency by enclosing all your operations in one transaction.

由于需要执行多个插入,因此数据库将被多次命中。在这种情况下,延迟是因为每次保存都是在不同的DB事务中完成的。您可以通过将所有操作封装在一个事务中来减少延迟。

class Foo
  belongs_to  :parent,   :class_name => "Foo"
  has_many    :children, :class_name => "Foo", :foreign_key=> "parent_id"
end

Your save method might look like this:

您的save方法可能是这样的:

# build the parent and the children
a = Foo.new(:name => "bar")
a.children.build(:name => "123")

b = Foo.new("baz")
b.children.build(:name => "zxy")

#save parents and their children in one transaction
Foo.transaction do
  a.save
  b.save
end

The save call on the parent object saves the child objects.

父对象上的save调用保存子对象。

#3


8  

One of the two answers found somewhere else: by Beerlington. Those two are your best bet for performance

在其他地方找到的两个答案之一:伯灵顿(Beerlington)著。这两个是你的最佳选择


I think your best bet performance-wise is going to be to use SQL, and bulk insert multiple rows per query. If you can build an INSERT statement that does something like:

我认为,最好的性能选择是使用SQL,每次查询大量插入多行。如果您可以构建一个INSERT语句,其功能如下:

INSERT INTO foos_bars (foo_id,bar_id) VALUES (1,1),(1,2),(1,3).... You should be able to insert thousands of rows in a single query. I didn't try your mass_habtm method, but it seems like you could to something like:

插入foos_bars(foo_id bar_id)值(1,1),(1、2),(1、3)....您应该能够在一个查询中插入数千行。我没有尝试过你的mass_habtm方法,但是看起来你可以这样做:


bars = Bar.find_all_by_some_attribute(:a) 
foo = Foo.create
values = bars.map {|bar| "(#{foo.id},#{bar.id})"}.join(",") 
connection.execute("INSERT INTO foos_bars (foo_id, bar_id) VALUES
#{values}")

Also, if you are searching Bar by "some_attribute", make sure you have that field indexed in your database.

此外,如果您正在使用“some_attribute”搜索Bar,请确保在数据库中对该字段进行了索引。


OR

You still might have a look at activerecord-import. It's right that it doesn't work without a model, but you could create a Model just for the import.

您仍然可以查看activerecord-import。没有模型是不行的,但是您可以为导入创建一个模型。


FooBar.import [:foo_id, :bar_id], [[1,2], [1,3]]

Cheers

干杯

#4


0  

You don't need a gem to hit DB fast and only once!

你不需要宝石就能快速命中DB,而且只有一次!

Jackrg has worked it out for us: https://gist.github.com/jackrg/76ade1724bd816292e4e

Jackrg为我们解决了这个问题:https://gist.github.com/jackrg/76ade1724bd816292e4e