Сохранение нескольких объектов в одном вызове в рельсах
У меня есть метод в рельсах, который делает что-то вроде этого:
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)
Это сделало бы все это только в двух образах базы данных. Есть ли простой способ сделать это в рельсах, или я застрял делать это по одному за раз?
Ответы
Ответ 1
Вы можете попробовать использовать Foo.create вместо Foo.new. Create "Создает объект (или несколько объектов) и сохраняет его в базе данных, если проходят проверки. Полученный объект возвращается, был ли объект успешно сохранен в базе данных или нет."
Вы можете создать несколько таких объектов:
# Create an Array of new objects
parents = Foo.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
Затем для каждого родителя вы также можете использовать create для добавления в свою ассоциацию:
parents.each do |parent|
parent.children.create (:child_name => 'abc')
end
Я рекомендую прочитать как документацию ActiveRecord, так и Rails Guides на ActiveRecord интерфейс запросов и ассоциации ActiveRecord. Последний содержит руководство по всем методам, получаемым классом при объявлении ассоциации.
Ответ 2
Поскольку вам нужно выполнить несколько вставок, база данных будет удалена несколько раз. Задержка в вашем случае заключается в том, что каждое сохранение выполняется в разных транзакциях БД. Вы можете уменьшить задержку, включив все свои операции в одну транзакцию.
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
вызов на родительский объект сохраняет дочерние объекты.
Ответ 3
Один из двух ответов найден в другом месте: Beerlington.
Эти два являются лучшим выбором для производительности
Я думаю, что наилучшим вариантом будет использование SQL, а массовая вставка нескольких строк для каждого запроса. Если вы можете создать инструкцию INSERT, которая делает что-то вроде:
INSERT INTO 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}")
Кроме того, если вы ищете панель "some_attribute", убедитесь, что вы указали это поле в своей базе данных.
ИЛИ
Вы все еще можете посмотреть на activerecord-import. Правильно, что он не работает без модели, но вы можете создать модель только для импорта.
FooBar.import [:foo_id, :bar_id], [[1,2], [1,3]]
Приветствия
Ответ 4
Вам не нужен драгоценный камень, чтобы быстро попасть в БД и только один раз!
Jackrg разработал для нас:
https://gist.github.com/jackrg/76ade1724bd816292e4e
Ответ 5
вам нужно использовать этот драгоценный камень "FastInserter" → https://github.com/joinhandshake/fast_inserter
и вставка большого количества и тысяч записей выполняется быстро, потому что этот драгоценный камень пропускает активную запись и использует только один SQL-запрос