16

我有两个表,在 Rails 中有一个 HABTM 关系。类似于以下内容:

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.1ms) SHOW FIELDS FROM bars_foos SQL (0.6ms) INSERT INTO bars_foos( bar_id, foo_id) VALUES (100, 117200)

我查看了 ar-extensions,但import如果没有模型 (Model.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)....

您应该能够在单个查询中插入数千行。我没有尝试你的 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 回答