如何用ActiveRecord / RailsexpressionNOT IN查询?

只是为了更新这个,因为似乎很多人都这样认为,如果您使用Rails 4,请查看TrungLê和VinniVidiVicci的答案。

Topic.where.not(forum_id:@forums.map(&:id)) Topic.where(published:true).where.not(forum_id:@forums.map(&:id)) 

我希望有一个简单的解决scheme,不涉及find_by_sql ,如果不是那么我想这将不得不工作。

我发现这篇文章引用了这个:

 Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) }) 

这是一样的

 SELECT * FROM topics WHERE forum_id IN (<@forum ids>) 

我想知道是否有办法不这样做,如:

 SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>) 

我正在使用这个:

 Topic.where('id NOT IN (?)',actions) 

在哪里actions是一个数组: [1,2,3,4,5]

编辑:

对于Rails 4表示法:

 Article.where.not(title: ['Rails 3', 'Rails 5']) 

仅供参考,在Rails 4中,您可以使用not语法:

 Article.where.not(title: ['Rails 3', 'Rails 5']) 

你可以尝试像这样:

 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) 

使用Arel:

 topics=Topic.arel_table Topic.where(topics[:forum_id].not_in(@forum_ids)) 

或者,如果优选的话:

 topics=Topic.arel_table Topic.where(topics[:forum_id].in(@forum_ids).not) 

并且由于轨道4在:

 topics=Topic.arel_table Topic.where.not(topics[:forum_id].in(@forum_ids)) 

请注意,最终你不希望forum_ids成为ID列表,而是一个子查询,如果是的话,那么你应该做这样的事情之前,得到的主题:

 @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*/) 

另外请注意,最终你不想这样做,而是一个连接 – 可能更有效。

要扩展@TrungLê的答案,在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为空,则接受的解决scheme失败。 为了解决这个问题,我不得不这样做

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

你可能想看看Ernie Miller的meta_where插件 。 你的SQL语句:

 SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>) 

…可以这样expression:

 Topic.where(:forum_id.nin => @forum_ids) 

Railscasts的Ryan Bates创build了一个很好的video解释MetaWhere 。

不知道这是你在找什么,但在我看来,它肯定比embedded式SQL查询看起来更好。

这些论坛id可以用实用的方式来制定吗? 例如,你能不能find这些论坛 – 如果是这样的话,你应该这样做

 Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null") 

哪一个比不进行SQL更有效率?

这种方式优化了可读性,但在数据库查询方面效率不高:

 # Retrieve all topics, then use array subtraction to # find the ones not in our list Topic.all - @forums.map(&:id) 

原来的文章特别提到使用数字ID,但是我来到这里寻找使用string数组做NOT IN的语法。

ActiveRecord也会为你处理:

 Thing.where(['state NOT IN (?)', %w{state1 state2}]) 

你可以在你的条件下使用sql:

 Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)]) 

搭载jonnii:

 Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.pluck(:id)]) 

使用采摘而不是映射元素

通过railsconf 2012find10个你不知道rails的东西可以做的事情

当你查询一个空数组时,将“<< 0”添加到where数组中的数组中,以便它不返回“NULL”并中断查询。

 Topic.where('id not in (?)',actions << 0) 

如果操作可能是空的或空白的数组。

这是一个更复杂的“不在”查询,在使用squeel的rails 4中使用子查询。 当然,相当于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的其他范围。 <<是squeel中的not in运算符。

希望这有助于某人。