我希望有一个简单的解决方案,不涉及
find_by_sql
,如果没有,那么我想这必须起作用。
我发现这篇文章引用了这个:
Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })
与
相同SELECT * FROM topics WHERE forum_id IN (<@forum ids>)
我想知道是否有办法做到这一点
NOT IN
,例如:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
轨道 4+:
Article.where.not(title: ['Rails 3', 'Rails 5'])
轨道 3:
Topic.where('id NOT IN (?)', Array.wrap(actions))
其中
actions
是一个数组,其中: [1,2,3,4,5]
仅供参考,在 Rails 4 中,您可以使用
not
语法:
Article.where.not(title: ['Rails 3', 'Rails 5'])
自 Rails 4 起:
topics=Topic.arel_table
Topic.where.not(forum_id: @forum_ids)
请注意,最终您不希望forum_ids成为ids列表,而是一个子查询,如果是这样,那么您应该在获取主题之前执行类似的操作:
@forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)
通过这种方式,您可以在单个查询中获得所有内容:类似于:
select * from topic
where forum_id in (select id
from forum
where /*whatever conditions are desirable*/)
还要注意,最终您不想这样做,而是想进行连接 - 这可能会更有效。
你可以尝试这样的事情:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.map(&:id)])
您可能需要做
@forums.map(&:id).join(',')
。我不记得 Rails 是否会将参数放入 CSV 列表(如果它是可枚举的)。
你也可以这样做:
# in topic.rb
named_scope :not_in_forums, lambda { |forums| { :conditions => ['forum_id not in (?)', forums.select(&:id).join(',')] }
# in your controller
Topic.not_in_forums(@forums)
要扩展@Trung Lê 的答案,在 Rails 4 中您可以执行以下操作:
Topic.where.not(forum_id:@forums.map(&:id))
你还可以更进一步。 如果您需要首先过滤仅发布的主题,然后然后过滤掉您不需要的ID,您可以这样做:
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
Rails 4 让一切变得如此简单!
如果
@forums
为空,则接受的解决方案失败。为了解决这个问题,我必须这样做
Topic.find(:all, :conditions => ['forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id))])
或者,如果使用 Rails 3+:
Topic.where( 'forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id)) ).all
上面的大多数答案应该足以满足您的需求,但如果您要做更多这样的谓词和复杂的组合,请查看Squeel。您将能够执行以下操作:
Topic.where{{forum_id.not_in => @forums.map(&:id)}}
Topic.where{forum_id.not_in @forums.map(&:id)}
Topic.where{forum_id << @forums.map(&:id)}
这些论坛id能否以务实的方式制定出来?例如你能以某种方式找到这些论坛吗 - 如果是这种情况你应该做类似的事情
Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")
这比执行 SQL 更高效
not in
这种方式优化了可读性,但在数据库查询方面效率不高:
# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - @forums.map(&:id)
您可能想看看 Ernie Miller 的 meta_where 插件。您的 SQL 语句:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
...可以这样表达:
Topic.where(:forum_id.nin => @forum_ids)
Railscasts 的 Ryan Bates 创建了一个精彩的截屏视频,解释了 MetaWhere。
不确定这是否是您正在寻找的,但在我看来,它肯定比嵌入式 SQL 查询更好。
原帖特别提到了使用数字 ID,但我来这里是为了寻找对字符串数组执行 NOT IN 的语法。
ActiveRecord 也会很好地为你处理这个问题:
Thing.where(['state NOT IN (?)', %w{state1 state2}])
您可以在您的条件下使用sql:
Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)])
搭载乔尼:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.pluck(:id)])
使用 pluck 而不是映射元素
查询空白数组时添加“<< 0" to the array in the where block so it doesn't return "NULL" and break the query.
Topic.where('id not in (?)',actions << 0)
如果操作可以是空数组或空白数组。
这是一个更复杂的“not in”查询,使用 Rails 4 中的子查询使用 squeel。当然,与等效的 sql 相比非常慢,但是嘿,它有效。
scope :translations_not_in_english, ->(calmapp_version_id, language_iso_code){
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == 'en' }.
where{ cavtl1.calmapp_version_id == my{calmapp_version_id}}.
where{ dot_key_code << (Translation.
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == my{language_iso_code} }.
select{ "dot_key_code" }.all)}
}
作用域中的前 2 个方法是声明别名 cavtl1 和 tl1 的其他作用域。 << is the not in operator in squeel.
希望这对某人有帮助。
如果有人想使用两个或更多条件,您可以这样做:
your_array = [1,2,3,4]
your_string = "SOMETHING"
YourModel.where('variable1 NOT IN (?) AND variable2=(?)',Array.wrap(your_array),your_string)