让Addresse
和List
具有多对多关系,如下所示。
有时需要所有List
和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
答案 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}
)