在 Rails 中的一次调用中保存多个对象
我在 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
问题是我添加的实体越多,这需要越来越长的时间。我怀疑这是因为它必须为每条记录访问数据库。由于它们是嵌套的,我知道在父母被拯救之前我无法拯救孩子,但我想立即拯救所有父母,然后是所有孩子。最好做这样的事情:
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)
只需两次数据库点击就可以完成这一切。有没有一种简单的方法可以在 Rails 中做到这一点,或者我只能一次做一个?
I have a method in rails that is doing something like this:
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
由于需要执行多次插入,数据库将被多次命中。您的情况的延迟是因为每次保存都是在不同的数据库事务中完成的。您可以通过将所有操作包含在一个事务中来减少延迟。
您的 save 方法可能如下所示:
对父对象的
save
调用将保存子对象。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.
Your save method might look like this:
The
save
call on the parent object saves the child objects.您可以尝试使用 Foo.create 而不是 Foo.new。创建“如果验证通过,则创建一个对象(或多个对象)并将其保存到数据库。无论该对象是否成功保存到数据库,都会返回结果对象。”
您可以像这样创建多个对象:
然后,对于每个父对象,您还可以使用 create 添加到其关联:
我建议阅读 ActiveRecord 文档 以及 ActiveRecord 查询接口 上的 Rails 指南 a> 和 ActiveRecord 关联。后者包含声明关联时类获得的所有方法的指南。
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."
You can create multiple objects like this:
Then, for each parent, you can also use create to add to its association:
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.
insert_all(Rails 6+)
Rails 6
引入了一种新方法insert_all,它通过单个SQL INSERT
语句将多条记录插入数据库。此外,此方法不会实例化任何模型,并且不会调用 Active Record 回调或验证。
因此,
效率要高得多。
它比您只想插入新记录的
insert_all (Rails 6+)
Rails 6
introduced a new method insert_all, which inserts multiple records into the database in a singleSQL INSERT
statement.Also, this method does not instantiate any models and does not call Active Record callbacks or validations.
So,
it is significantly more efficient than
if all you want to do is to insert new records.
在其他地方找到的两个答案之一:由 Beerlington 找到。
这两个是性能方面的最佳选择,
我认为性能方面的最佳选择是使用 SQL,并在每个查询中批量插入多行。如果您可以构建一个执行以下操作的 INSERT 语句:
INSERT INTO foos_bars (foo_id,bar_id) VALUES (1,1),(1,2),(1,3)....
您应该能够在单个查询中插入数千行。我没有尝试你的 Mass_habtm 方法,但似乎你可以这样做:
另外,如果你通过“some_attribute”搜索 Bar,请确保你在数据库中索引了该字段。
或者
您仍然可以看看 activerecord-import。没错,没有模型它就无法工作,但您可以仅为导入创建一个模型。
干杯
One of the two answers found somewhere else: by Beerlington.
Those two are your best bet for performance
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:
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:
Also, if you are searching Bar by "some_attribute", make sure you have that field indexed in your database.
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.
Cheers
你需要使用这个 gem“FastInserter”-> https://github.com/joinhandshake/fast_inserter
并且插入大量和数千条记录很快因为这个 gem 跳过活动记录,并且只使用单个 sql 原始查询
you need to use this 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
您不需要宝石就能快速击中 DB,而且只需一次!
Jackrg 已经为我们解决了这个问题:
https://gist.github.com/jackrg/76ade1724bd816292e4e
You don't need a gem to hit DB fast and only once!
Jackrg has worked it out for us:
https://gist.github.com/jackrg/76ade1724bd816292e4e