16

Rails で HABTM 関係を持つ 2 つのテーブルがあります。次のようなもの:

class Foo < ActiveRecord::Base
  has_and_belongs_to_many :bars
end

class Bar < ActiveRecord::Base
  has_and_belongs_to_many :foos
end

今、私は新しいFooオブジェクトを持っており、事前にロードした何千ものバーをそれに一括割り当てしたいと考えています:

@foo = Foo.create
@bars = Bar.find_all_by_some_attribute(:a)

これを行う最速の方法は何ですか? 私はもう試した:

@foo.bars = @bars
@foo.bars << @bars

そして、どちらも非常に遅く、それぞれに次のようなエントリがありますbar:

bar_foos 列 (1.1 ミリ秒) bars_foos SQL からフィールドを表示 (0.6 ミリ秒) INSERT INTO bars_foos( bar_id, foo_id) 値 (100, 117200)

私は ar-extensions を見ましたがimport、結合テーブルでの使用を妨げるモデル (Model.import) なしでは関数は機能しないようです。

SQL を書く必要がありますか、それとも Rails にはもっときれいな方法がありますか?

4

4 に答える 4

10

パフォーマンス上の最善の策は、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」で Bar を検索する場合は、そのフィールドがデータベースにインデックスされていることを確認してください。

于 2010-02-08T03:20:51.137 に答える
7

あなたはまだactiverecord-importを見ているかもしれません。モデルがないと機能しないのは正しいですが、インポート専用のモデルを作成することもできます。

class FooBar < ActiveRecord::Base; end

FooBar.import [:foo_id, :bar_id], [[1,2], [1,3]]

次のように、これをトランザクションでラップして、HABTMが完全に入力されるようにすることができます。

ActiveRecord::Base.transaction do
  imported_foo = Foo.import( foo_names, foo_values )
  imported_bar = Bar.import( bar_names, bar_values )
  FooBar.import( [:foo_id, :bar_id], imported_foo.ids.zip(imported_bar.ids)
end
于 2011-07-13T14:18:50.047 に答える
1

これは、同等のネイティブRailsコードよりも7倍高速でした。

class << Foo
  def mass_habtm(attr_array)
    attr_str = attr_array.map{|a| %Q{'#{a}'} }.uniq.join(",")
    self.connection.execute(%Q{insert into foos_bars (foo_id,bar_id) 
                     select distinct foos.id,bars.id from foos,bars 
                     where foos.id = #{self.id} 
                     and bars.some_attribute in (#{attr_str})})
  end
end

これは、Railsで効率的にサポートされるのに十分簡単な操作であるように思われます。誰かがよりクリーンな方法を持っているかどうか、聞いてみたいと思います。

私は2.2.2を実行していますが、3.xでより効率的に実装されている可能性がありますか? 3.0.2でも同じことがわかりました。

于 2010-02-05T00:00:13.417 に答える
-4

正直なところ、has_and_belongs_to_many非常に時代遅れのやり方です。has_many :throughおそらく、結合テーブルを行う新しい方法であり、かなり長い間使用されてきた を調べる必要があります。

class Foo < ActiveRecord::Base
  has_many :foobars
  has_many :bars, :through => :foobars

  def add_many_bars(bars)
    bars.each do |bar|
      self.bars << bar
    end
  end
end

class Bar < ActiveRecord::Base
  has_many :foobars
  has_many :foos, :through => :foobars
end

class FooBar < ActiveRecord::Base
  belongs_to :foo
  belongs_to :bar
end

また、本番環境では同じものを実行してみて、どのようなパフォーマンスが得られるかを確認する必要があります。これは、開発環境では必ずしも発生しないキャッシュが本番環境で行われるためです。

于 2010-02-06T19:24:51.977 に答える