Эффективные запросы соединения - это можно сделать с помощью ActiveRecord - PullRequest
4 голосов
/ 10 января 2012

У меня 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 %>

Какой лучший способ получить All C для A? Какой лучший способ получить все D для A?

Я хочу получить все 'C', используя предложение order_by со значением "creation_at" вместо итерации по B.

Может быть, мне не хватает волшебства ActiveRecord?

Я ценю любую помощь.

1 Ответ

6 голосов
/ 10 января 2012

Прежде всего, вам нужно внести пару изменений.

  1. class C нужна связь с D

    class C < ActiveRecord::Base
      belongs_to :B
      has_one :D
    end
    
  2. Если вы хотите получить доступ к A D, вам также необходимо указать это.

    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 знает, что вы захотите получить доступ к A C с помощью вызова include, и генерирует минимальное количество запросов. То же самое касается D:

-> 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)

Скажем, вы хотели все A D, но хотели 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...