У меня есть метод в рельсах, который делает что-то вроде этого:
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)
Это сделает все за два обращения к базе данных. Есть ли простой способ сделать это в рельсах, или я застрял, делая это по одному?
источник
ActiveRecord::Base.transaction { records.each(&:save) }
или подобным вы можете, по крайней мере, поместить все INSERT или UPDATE в одну транзакцию.Поскольку вам нужно выполнить несколько вставок, база данных будет поражена несколько раз. Задержка в вашем случае связана с тем, что каждое сохранение выполняется в разных транзакциях БД. Вы можете уменьшить задержку, заключив все свои операции в одну транзакцию.
class Foo belongs_to :parent, :class_name => "Foo" has_many :children, :class_name => "Foo", :foreign_key=> "parent_id" end
Ваш метод сохранения может выглядеть так:
# 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
save
Вызов на родительский объект сохраняет дочерние объекты.источник
insert_all (Rails 6+)
Rails 6
представил новый метод insert_all , который вставляет несколько записей в базу данных однимSQL INSERT
оператором.Кроме того, этот метод не создает экземпляров каких-либо моделей и не вызывает обратные вызовы или проверки Active Record.
Так,
Foo.insert_all([ { first_name: 'Jamie' }, { first_name: 'Jeremy' } ])
это значительно эффективнее, чем
Foo.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
если все, что вы хотите сделать, это вставить новые записи.
источник
Один из двух ответов, найденных где-то еще: Бирлингтоном . Эти двое - лучший выбор для производительности
Я думаю, что ваш лучший выбор с точки зрения производительности - использовать SQL и массовую вставку нескольких строк на запрос. Если вы можете создать оператор INSERT, который делает что-то вроде:
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}")
Кроме того, если вы ищете Bar по «some_attribute», убедитесь, что это поле проиндексировано в вашей базе данных.
ИЛИ
Вы все еще можете взглянуть на activerecord-import. Это правильно, что без модели это не работает, но вы можете создать модель только для импорта.
FooBar.import [:foo_id, :bar_id], [[1,2], [1,3]]
Ура
источник
вам нужно использовать этот гем "FastInserter" -> https://github.com/joinhandshake/fast_inserter
и вставка большого количества и тысяч записей выполняется быстро, потому что этот гем пропускает активную запись и использует только один необработанный запрос sql
источник
Вам не нужен драгоценный камень, чтобы быстро и только один раз попасть в БД!
Джекрг разработал это для нас: https://gist.github.com/jackrg/76ade1724bd816292e4e
источник