在没有条件的情况下使用LEFT OUTER JOIN来进行急切的加载关联?

时间:2012-12-17 13:49:15

标签: sql ruby-on-rails ruby ruby-on-rails-3.1

我们有一个设置了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而不是默认的双查询方法?

3 个答案:

答案 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而不是单独的查询