102

私はこのようなことをしているレールにメソッドを持っています:

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)

たった 2 回のデータベース ヒットですべてが完了します。レールでこれを行う簡単な方法はありますか?

4

6 に答える 6

105

複数の挿入を実行する必要があるため、データベースは複数回ヒットします。あなたの場合の遅延は、各保存が異なる DB トランザクションで行われるためです。すべての操作を 1 つのトランザクションに含めることで、待ち時間を短縮できます。

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

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

親オブジェクトを呼び出すと、save子オブジェクトが保存されます。

于 2010-03-24T18:05:52.233 に答える
73

Foo.new の代わりに Foo.create を使用してみてください。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 on ActiveRecord query interface and ActiveRecord associationsの両方を読むことをお勧めします。後者には、関連付けを宣言したときにクラスが取得するすべてのメソッドのガイドが含まれています。

于 2010-03-24T16:18:17.773 に答える
11

他の場所で見つかった2つの答えの1つ:Beerlingtonによる。これらの2つはパフォーマンスのためのあなたの最善の策です


パフォーマンス面での最善の策は、SQLを使用し、クエリごとに複数の行を一括挿入することだと思います。次のようなことを行うINSERTステートメントを作成できる場合:

INSERT INTO foos_bars(foo_id、bar_id)VALUES(1,1)、(1,2)、(1,3)....1つのクエリで数千の行を挿入できるはずです。私はあなたの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]]

乾杯

于 2012-03-12T05:07:50.397 に答える
0

この宝石「FastInserter」を使用する必要があります-> https://github.com/joinhandshake/fast_inserter

このgemはアクティブなレコードをスキップし、単一のSQL生クエリのみを使用するため、多数の数千のレコードを挿入するのは高速です

于 2019-01-25T18:44:11.287 に答える
-3

DB をすばやく 1 回だけヒットするのに gem は必要ありません。

Jackrg が解決してくれました: https://gist.github.com/jackrg/76ade1724bd816292e4e

于 2014-08-21T11:13:32.967 に答える