我们有一个设置了has_one关联的模型:
class User
has_one :shirt
class Shirt
belongs_to :user
目前,我们可以在检索有限数量的用户时添加.includes(:shirt)
,并按预期执行两次SQL查询。
我们遇到的问题是加载页面的查询是这样做的:
SELECT "shirt".* FROM "shirts" WHERE "shirt"."user_id" IN (2147521, 2147522 ... )
当检索50个用户和衬衫时,这对我们来说效率不高。我们的用户和衬衫桌很大。我们注意到,通过强制Rails使用INNER JOIN而不是通过执行以下操作来提高速度:
User.where( ... ).joins(:shirts).includes(:shirts).limit(50)
不幸的是,这只会返回有衬衫的用户。我们需要能够返回有限数量的用户,无论他们是否有相关的衬衫。
有没有办法强制Rails使用LEFT OUTER JOIN而不是默认的双查询方法来加载关联对象?
添加where
子句,返回true,关联对象的值是否为null。似乎并不介意是否没有关联的对象,至少在Postgres上。
User.where( ... ).includes(:shirt).where("shirts.created_at IS NULL OR shirts.created_at IS NOT NULL")
这解决了这个问题,但它并不理想,是不是有办法使用LEFT OUTER JOIN而不是默认的双查询方法?
答案 0 :(得分:2)
如this post中所述,对于大多数情况,2查询方法被认为更快(尽管对于一对一的关系不一定如此)。如果要强制Rails使用LEFT OUTER JOIN,可以通过在关联表上添加无意义的过滤器来实现此目的:
User.where( ... ).
includes(:shirts).
where("shirts.id IS NULL OR shirts.id = shirts.id").
limit(50)
答案 1 :(得分:0)
如果你有一对一的关联,就像上面的例子一样,你可以使用eager_load
方法,如User.eager_load(:shirt)
,它会使用LEFT OUTER JOIN
,所以你将成为仅使用1个查询就可以使用user.shirt
,但我不确定它是否为rails 4的新功能。
答案 2 :(得分:0)
应用于Rails4:
User.where( ... ).includes(:shirts).references(:shirts)
或
User.where( ... ).eager_load(:shirts)
会强制LEFT OUTER JOIN
而不是单独的查询