+1  A: 
klochner
A: 

Honestly, has_and_belongs_to_many is a very antiquated way of doing things. You should probably look into has_many :through, which is the new way of doing join tables, and has been for quite some time.

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

Also, you should try running the same in production and see what kind of performance you get, as a lot of caching goes on in production that doesn't necessarily occur in development.

Josh
Not to be a jerk, but you in no way addressed the main question - the speed of creating the relations, other than speculating that production might be better. While caching may help, it almost certainly won't change the way the SQL is formulated. I'd also argue that habtm is a better candidate for optimizing this stuff, since has_many->through requires a model class, which means there may be callbacks in the join model.
klochner
And I'm pretty sure your implementation is *slower* than the one I had presented as *not being fast enough*.
klochner
Yes, but having a model on the join allows you to do other things with finders and other options that you may not have had access to. At least this way, you have another model to put your code in rather than repeating it in both models if you want to go back and forth.
Josh
+3  A: 

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:

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}")

Also, if you are searching Bar by "some_attribute", make sure you have that field indexed in your database.

Beerlington
my mass_habtm just combines the queries into a single search/insert query, which probably doesn't gain *that* much over what you have here. I hate selecting my own, so thanks for at least giving me a viable option.
klochner