2010-02-04 67 views
13

我有兩個表,在Rails中有一個HABTM關係。像下面的內容:在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以下的條目:

bars_foos列(1.1ms)顯示 FIELDS FROM bars_foos SQL(0.6ms) INSERT INTO bars_foosbar_idfoo_id)VALUES(100,117200)

我看着ar-extensions,但import函數在沒有模型(Model.import)的情況下似乎無法使用,該模型不適用於連接表。

我需要編寫SQL,還是Rails有一個更漂亮的方法?

+0

真的嗎?沒有人?你們跳過所有的上籃和「最佳練習」問題:) – klochner 2010-02-04 22:32:30

回答

6

我認爲你最好的性能表現就是使用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」欄,請確保您有場在你的數據庫索引。

+0

我的mass_habtm只是將查詢合併到一個搜索/插入查詢中,這可能不會獲得*,遠遠超過你在這裏的內容。我討厭選擇我自己的,所以感謝至少給我一個可行的選擇。 – klochner 2010-02-08 17:28:01

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上發現相同。

-3

老實說,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 

此外,你應該嘗試在生產環境中運行相同的,看看你會得到什麼樣的表現,因爲很多緩存在生產中並不一定發生在開發流程。

+1

不是一個混蛋,但你絕不會解決主要問題 - 創建關係的速度,除了猜測生產可能會更好。雖然緩存可能有所幫助,但它幾乎肯定不會改變SQL的制定方式。我也認爲habtm是優化這個東西的更好的選擇,因爲has_many-> through需要一個模型類,這意味着在連接模型中可能會有回調。 – klochner 2010-02-06 21:02:41

+1

我相當肯定你的執行速度*比我提出的速度慢*速度不夠快*。 – klochner 2010-02-06 21:10:48

+0

是的,但是在連接上有一個模型可以讓您使用您可能無法訪問的查找器和其他選項來執行其他操作。至少在這種情況下,如果你想來回穿梭,你可以使用另一種模式來放置代碼,而不是在兩種模型中重複使用。 – 2010-02-06 21:44:30

5

你仍然可以看看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 
相關問題