【问题标题】:Efficient Join queries - Can it be done using ActiveRecord高效的联接查询 - 可以使用 ActiveRecord 完成吗
【发布时间】:2012-02-06 14:00:14
【问题描述】:

我有 4 个模型,A、B、C 和 D

class A < ActiveRecord::Base
  has_many :B
  has_many :C, :through => :B
end  

class B < ActiveRecord::Base
  belongs_to :A  
  has_many   :C
  has_many   :D, :through => :C
end  

class C < ActiveRecord::Base    
  belongs_to :B
end    

class D < ActiveRecord::Base    
  belongs_to :C
end    

我有一个非常幼稚的实现,非常明显......

<% A.B.each do |b| %>
  <%= b.number %>
  <% b.C.each do |c| %>
    <%= c.name %>
  <% end %>
<% end %>

什么是 A 获得 All C 的最佳方式? 获得All D for A的最佳方法是什么?

我想使用带有“created_at”值的 order_by 子句获取所有“C”,而不是遍历 B。

可能是我缺少一些 ActiveRecord 魔法吗?

感谢您的帮助。

【问题讨论】:

  • 因为A通过B与C有关系,我也可以直接做A.C对吧?

标签: ruby-on-rails activerecord join model-associations


【解决方案1】:

首先,您需要进行一些更改。

  1. class C 需要与D 关联

    class C < ActiveRecord::Base
      belongs_to :B
      has_one :D
    end
    
  2. 如果你想访问AD,你也需要指定这个。

    class A < ActiveRecord::Base
      has_many :B
      has_many :C, :through => :B
      has_many :D, :through => :C
    end
    

现在,访问A 的所有C 的:

-> a = A.where(:id => 1).includes(:C).first
  A Load (0.2ms)  SELECT "as".* FROM "as" WHERE "as"."id" = 1 LIMIT 1
  B Load (0.1ms)  SELECT "bs".* FROM "bs" WHERE "bs"."a_id" IN (1)
  C Load (0.1ms)  SELECT "cs".* FROM "cs" WHERE "cs"."b_id" IN (1, 2)
 => #<A id: 1, created_at: "2012-01-10 04:28:42", updated_at: "2012-01-10 04:28:42"> 
-> a.C
 => [#<C id: 1, b_id: 1, created_at: "2012-01-10 04:30:10", updated_at: "2012-01-10 04:30:10">, #<C id: 2, b_id: 1, created_at: "2012-01-10 04:30:11", updated_at: "2012-01-10 04:30:11">, #<C id: 3, b_id: 2, created_at: "2012-01-10 04:30:21", updated_at: "2012-01-10 04:30:21">, #<C id: 4, b_id: 2, created_at: "2012-01-10 04:30:21", updated_at: "2012-01-10 04:30:21">]

请注意,当您调用 a.C 时不会执行另一个查询。这是因为 ActiveRecord 知道您将希望通过include 调用访问找到的AC,并生成最少数量的查询。 D's 也是如此:

-> a = A.where(:id => 1).includes(:D).first
  A Load (0.1ms)  SELECT "as".* FROM "as" WHERE "as"."id" = 1 LIMIT 1
  B Load (0.1ms)  SELECT "bs".* FROM "bs" WHERE "bs"."a_id" IN (1)
  C Load (0.1ms)  SELECT "cs".* FROM "cs" WHERE "cs"."b_id" IN (1, 2)
  D Load (0.1ms)  SELECT "ds".* FROM "ds" WHERE "ds"."c_id" IN (1, 2, 3, 4)

假设您想要所有AD 但想要订购C

A.where(:id => 1).includes(:C).order('cs.created_at DESC').includes(:D)

注意,您也可以将其设置为关联的默认值:

:order 选项规定了接收关联对象的顺序(在 SQL ORDER BY 子句使用的语法中)。

class Customer < ActiveRecord::Base
  has_many :orders, :order => "date_confirmed DESC"
end

【讨论】:

  • @iWasRobbed 谢谢! B通过一个非常悲伤的人有很多表情。 (现在我无法停止看到它们。)
  • 布兰登谢谢。我会试试这个,但我认为有关联可以解决这个问题。
  • 你可以不使用includes访问子对象,但是Rails会产生很多查询。你应该使用includes 来避免the N+1 problem
  • Brandon,这确实是一个更好更快的解决方案。谢谢。
猜你喜欢
  • 1970-01-01
  • 2017-12-23
  • 1970-01-01
  • 1970-01-01
  • 2013-02-10
  • 1970-01-01
  • 2018-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多