2011-09-28 101 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),那么它可能不会在任何方面产生巨大的影响。