Ruby-on-rails 在 rails 中的单个调用中保存多个对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2509320/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Saving multiple objects in a single call in rails
提问by captncraig
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 中做到这一点,还是我一次只能做一个?
采纳答案by Roadmaster
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.create 而不是 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 documentationand the Rails Guides on ActiveRecord query interfaceand ActiveRecord associations. The latter contains a guide of all the methods a class gains when you declare an association.
我建议阅读ActiveRecord 文档和有关ActiveRecord 查询接口和ActiveRecord 关联的 Rails 指南。后者包含当您声明关联时类获得的所有方法的指南。
回答by Harish Shetty
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.
由于您需要执行多次插入,因此数据库将被多次命中。您的情况延迟是因为每次保存都是在不同的数据库事务中完成的。您可以通过将所有操作包含在一个事务中来减少延迟。
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:
您的保存方法可能如下所示:
# 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 savecall on the parent object saves the child objects.
save对父对象的调用保存子对象。
回答by Nguyen Chien Cong
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:
INSERT INTO foos_bars (foo_id,bar_id) VALUES (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
干杯
回答by Marian13
If you are reading this post in 2020. There is good news.
如果您在 2020 年阅读这篇文章。有个好消息。
Rails 6introduced a new method insert_all, which inserts multiple records into the database in a single SQL INSERTstatement.
Rails 6引入了一个新方法insert_all,它在单个SQL INSERT语句中将多条记录插入到数据库中。
Also, this method does not instantiate any models and does not call Active Record callbacks or validations.
此外,此方法不会实例化任何模型,也不会调用 Active Record 回调或验证。
So, it is significantly more efficient than
因此,它的效率明显高于
Foo.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
if all you want to do is to insert new records.
如果您只想插入新记录。
回答by Fernando Fabreti
You don't need a gem to hit DB fast and only once!
您不需要 gem 即可快速命中 DB,而且只需一次!
Hymanrg has worked it out for us: https://gist.github.com/Hymanrg/76ade1724bd816292e4e
Hymanrg 为我们解决了这个问题:https://gist.github.com/Hymanrg/76ade1724bd816292e4e
回答by val caro
you need to use this gem "FastInserter" -> https://github.com/joinhandshake/fast_inserter
你需要使用这个 gem "FastInserter" -> https://github.com/joinhandshake/fast_inserter
and inserting a large number and thousands of records is fast because this gem skips active record, and only uses a single sql raw query
并且插入大量和数千条记录很快,因为这个 gem 跳过活动记录,并且只使用单个 sql 原始查询

