如何用ActiveRecord / Rails表达NOT IN查询?

只是为了更新这个,因为似乎有很多人来到这里,如果你使用Rails 4,请查看TrungLê`和VinniVidiVicci的答案。
Topic.where.not(forum_id:@forums.map(&:id))

Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
我希望有一个简单的解决方案,不涉及
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>)
    
已邀请:
我正在使用这个:
Topic.where('id NOT IN (?)', Array.wrap(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
为空,则接受的解决方案将失败。要解决这个问题,我必须这样做
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>)
......可以这样表达:
Topic.where(:forum_id.nin => @forum_ids)
Railscasts的Ryan Bates创建了一个很好的截屏视频,解释了MetaWhere。 不确定这是否是您正在寻找的东西,但在我看来它肯定比嵌入式SQL查询更好。     
这些论坛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)
    
原帖专门提到了使用数字ID,但我来到这里寻找使用字符串数组进行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)])
使用pluck而不是映射元素 通过railsconf 2012发现10件你不知道rails可以做的事情     
当您查询空白数组时,将“&lt;&lt; 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)}
    }
范围中的前两个方法是声明别名cavtl1和tl1的其他范围。 &LT;&LT;是不是在运营商的口号。 希望这有助于某人。     

要回复问题请先登录注册