是否有ActiveRecord方法来执行此SQL查询?

时间:2009-08-04 19:55:45

标签: ruby-on-rails ruby activerecord

AddresseList具有多对多关系,如下所示。

有时需要所有ListAddress不在。

使用显示的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 个答案:

答案 0 :(得分:3)

我会将此作为List

中的范围
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的其他方法,请参阅Sam Saffron的要点:http://gist.github.com/162489

答案 1 :(得分:1)

WHERE (address_list_memberships.address_id <> 13896)
在具有21849个地址和1483个列表的数据库中,

将会很昂贵。

翻转你的逻辑:

def lists_not_in
  List.all - self.lists
end

这样你只需从另一个数组中减去一个数组,而不是检查数据库中的每个记录,看它是否在列表中。

答案 2 :(得分:1)

您不会从ActiveRecord获得直接SQL获得的灵活性,特别是,您无法在活动记录中制作not in子句。

如果你想获得更多控制权,你可以尝试使用续集http://sequel.rubyforge.org/或只是手工制作。

注意,您拥有的解决方案存在风险,因为您允许进行SQL注入。 (a.id = #{address_id}