Есть ли ActiveRecord способ сделать этот запрос SQL? - PullRequest
0 голосов
/ 04 августа 2009

Имеют Addresse с и List с отношениями «многие ко многим», как показано ниже.

Иногда нужны все List s, а Address нет.

Используя показанный запрос find_by_sql, он отлично работает. Но есть ли способ сделать это без использования прямого SQL?

class List
  has_many :address_list_memberships
  has_many :addresses, :through => :address_list_memberships
end


class Address
  has_many :address_list_memberships, :dependent => :destroy
  has_many :lists, :through => :address_list_memberships

  # Lists that this Address is not in
  def Address.lists_not_in(address_id)
    sql = %Q|
SELECT
  l.*
FROM
  lists l
WHERE
  l.id
NOT IN
(
  SELECT
    l.id
  FROM
    addresses a, lists l, address_list_memberships alm
  WHERE
    a.id = alm.address_id AND l.id = alm.list_id
  AND
    a.id = #{address_id}
)
|
    List.find_by_sql(sql)
  end
end

Ответы [ 3 ]

3 голосов
/ 05 августа 2009

Я бы сделал это как область видимости в Списке

class List
  named_scope :without_address, lambda { |address_id| { :joins => 'inner join address_list_memberships alm on alm.list_id = lists.id', :conditions => ['alm.address_id <> ?', address_id]}}
end

Теперь вы можете вызывать List.without_address (4), и вы можете также вызывать области действия.

Как указывает Matchu, вы можете сделать это без написания SQL-кода соединения:

class List
  named_scope :without_address, lambda { |address_id| { :joins => :address_list_memberships, :conditions => ['address_list_memberships.address_id <> ?', address_id]}}
end

И убедитесь, что у вашей таблицы соединений есть индексы!

В миграции:

add_index "address_list_memberships", "address_id"
add_index "address_list_memberships", "list_id"

Другие способы форматирования named_scope см. В описании Сэма Саффрона: http://gist.github.com/162489

1 голос
/ 06 августа 2009

Вы не получите гибкости, которую вы получаете с прямым SQL от ActiveRecord, в частности, вы не сможете создать предложение not in в активной записи.

Если вы хотите получить немного больше контроля, вы можете попробовать использовать Sequel http://sequel.rubyforge.org/ или просто ручное ремесло.

Обратите внимание, что решение, которое у вас есть, рискованно, потому что вы допускаете инъекцию sql. (a.id = #{address_id})

1 голос
/ 05 августа 2009
WHERE (address_list_memberships.address_id <> 13896)

будет дорогостоящим для базы данных с 21849 адресами и 1483 списками.

Переверните свою логику:

def lists_not_in
  List.all - self.lists
end

Таким образом, вы только вычитаете один массив из другого, а не проверяете каждую запись в базе данных, чтобы увидеть, есть ли она в списке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...