2011-09-28 106 views
2

考慮以下模型:如何使用包含3個模型的w包含ActiveRecord?

Room (id, title, suggested) 
    has_many :room_apps, :dependent => :destroy 

RoomApp (room_id, app_id, appable_id, appable_type) 
    belongs_to :appable, :polymorphic => true 
    has_many :colors, :as => :appable 
    has_many :shirts, :as => :appable 

Colors (room_id) 
    belongs_to :room 
    belongs_to :room_app 
    belongs_to :app 

我想要做的就是讓所有建議的房間。在我的控制,我有:

@suggested_rooms = Room.includes(:room_apps).find_all_by_suggested(true).first(5) 

問題這裏是包括不能正常使用該數據庫被擊中幾次:

Processing by PagesController#splash as HTML 
    Room Load (0.6ms) SELECT "rooms".* FROM "rooms" WHERE "rooms"."suggested" = 't' ORDER BY last_activity_at DESC 
    RoomApp Load (0.6ms) SELECT "room_apps".* FROM "room_apps" WHERE "room_apps"."published" = 't' AND ("room_apps".room_id IN (5,4,3)) ORDER BY created_at DESC 
    RoomApp Load (5.9ms) SELECT "room_apps".* FROM "room_apps" WHERE "room_apps"."published" = 't' AND "room_apps"."id" = 6 AND ("room_apps".room_id = 5) ORDER BY created_at DESC LIMIT 1 
    Color Load (0.4ms) SELECT "colors".* FROM "colors" WHERE "colors"."id" = 5 LIMIT 1 
    RoomApp Load (0.6ms) SELECT "room_apps".* FROM "room_apps" WHERE "room_apps"."published" = 't' AND "room_apps"."id" = 5 AND ("room_apps".room_id = 4) ORDER BY created_at DESC LIMIT 1 
    Color Load (0.4ms) SELECT "colors".* FROM "colors" WHERE "colors"."id" = 4 LIMIT 1 
    RoomApp Load (0.4ms) SELECT "room_apps".* FROM "room_apps" WHERE "room_apps"."published" = 't' AND "room_apps"."id" = 4 AND ("room_apps".room_id = 3) ORDER BY created_at DESC LIMIT 1 
    Color Load (0.3ms) SELECT "colors".* FROM "colors" WHERE "colors"."id" = 3 LIMIT 1 

是什麼設置不正確?我希望能夠獲得建議的房間,並使用包含room_apps的一次打擊與目前每個房間都有打擊的房間。

想法?由於

+0

你可以先交換(5)限制(5),看看是否改變了什麼? – Cluster

+0

沒有變化。新行等於:Room.includes(:room_apps).where(:suggested => true).limit(5) – AnApprentice

+1

你如何在你的視圖中遍歷'@ suggested_rooms'?另外,您可能還想用'.includes(:room_apps =>:colors)'來包含顏色。 – James

回答

0

我認爲你要麼需要使用完整Rails3中AREL接口,像這樣:

@suggested_rooms = Room.includes(:room_apps).where(:suggested => true).limit(5)

還是這樣做了Rails的2.3倍:

@suggested_rooms = Room.find_all_by_suggested(true, :include=>:room_apps).first(5)

+0

謝謝你這就是我現在所說的,在評論中提到過,但它並沒有對inlcludes做任何事情。我也嘗試過加入,沒有運氣 – AnApprentice

0

做了一些挖掘周圍,我想我有一個想法是怎麼回事。

包括默認情況下不會生成單個查詢。它生成N個查詢,其中N是包含的模型的數量。

ruby-1.9.2-p180 :014 > Room.where(:suggested => true).includes(:room_apps => :colors) 
    Room Load (0.5ms) SELECT "rooms".* FROM "rooms" WHERE "rooms"."suggested" = 't' 
    RoomApp Load (0.8ms) SELECT "room_apps".* FROM "room_apps" WHERE "room_apps"."room_id" IN (1) 
    Color Load (0.5ms) SELECT "colors".* FROM "colors" WHERE "colors"."room_app_id" IN (1) 

一個例外是,如果你有一個地方被列入引用模型表中的一個條款,在這種情況下,將使用LEFT OUTER JOIN where子句添加到該表。

如果你想INNER JOIN一堆模型包括它們,你必須使用兩個連接幷包含給定的模型。單獨加入只會在關係中進行INNER JOIN,包括將拉入領域並設置完整關係的返回模型。

ruby-1.9.2-p180 :015 > Room.where(:suggested => true).joins(:room_apps => :colors) 
    Room Load (0.8ms) SELECT "rooms".* 
        FROM "rooms" 
        INNER JOIN "room_apps" 
         ON "room_apps"."room_id" = "rooms"."id" 
        INNER JOIN "colors" 
         ON "colors"."room_app_id" = "room_apps"."id" 
        WHERE "rooms"."suggested" = 't' 

ruby-1.9.2-p180 :016 > Room.where(:suggested => true).joins(:room_apps => :colors).includes(:room_apps => :colors) 
    SQL (0.6ms) SELECT "rooms"."id" AS t0_r0, "rooms"."suggested" AS t0_r1, "rooms"."created_at" AS t0_r2, "rooms"."updated_at" AS t0_r3, "room_apps"."id" AS t1_r0, "room_apps"."room_id" AS t1_r1, "room_apps"."created_at" AS t1_r2, "room_apps"."updated_at" AS t1_r3, "colors"."id" AS t2_r0, "colors"."room_id" AS t2_r1, "colors"."room_app_id" AS t2_r2, "colors"."created_at" AS t2_r3, "colors"."updated_at" AS t2_r4 
       FROM "rooms" 
       INNER JOIN "room_apps" 
       ON "room_apps"."room_id" = "rooms"."id" 
       INNER JOIN "colors" 
       ON "colors"."room_app_id" = "room_apps"."id" 
       WHERE "rooms"."suggested" = 't' 

在最後一個查詢大令人費解的選擇部分是AREL確保從所有車型領域是獨一無二的,能夠區分時,他們需要被映射回實際的模型。

無論你是單獨使用包含還是包含連接,都會影響你帶回的數據量,以及如果你沒有進行INNER JOIN,可能會有多少速度差異,從而導致大量重複數據回。我會想象如果'房間'有十幾個字段,'顏色'有一個字段,但有100個顏色映射到一個房間,而不是總共拉回113個字段(1房間* 13 + 100色* 1)您最終會有1400個字段(13 + 1 * 100種顏色)。不完全是性能提升。

雖然單獨使用的不足之處在於,如果每個房間都有大量的顏色,IN(ID)將是巨大的,是一把雙刃劍。

下面是一個簡單的測試,我用不同的配置確實使用sqlite3的

我設置兩套客房,一用:建議=> true,則其他:建議=>假的。建議的房間在房間/房間應用/顏色之間的比例爲1:1:2,建議的虛擬房間以1:1:10的比例設置,建議和未建議的比例爲10:1。

# 100/10 rooms 
# insert only 
100 * 1/1/2: 8.1ms 
10 * 1/1/10: 3.2ms 

# insert + joins 
100 * 1/1/2: 6.2ms 
10 * 1/1/10: 3.1ms 

# 1000/100 rooms 
# insert only 
1000 * 1/1/2: 76.8ms 
100 * 1/1/10: 19.8ms 

# insert + joins 
1000 * 1/1/2: 54.5ms 
100 * 1/1/10: 23.1ms 

時代與自己無關,這是通過IRB在WinXP主機上的Ubuntu客戶機上運行在蹩腳的硬盤上。考慮到你已經有了一個限制(5),那麼它可能不會在任何方面產生巨大的影響。