我希望有一个简单的解决方案,不涉及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>)

当前回答

你可能想看看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查询更好。

其他回答

你可以尝试这样做:

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)

如果有人想使用两个或更多的条件,你可以这样做:

your_array = [1,2,3,4]
your_string = "SOMETHING"

YourModel.where('variable1 NOT IN (?) AND variable2=(?)',Array.wrap(your_array),your_string)

下面是一个更复杂的“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)}
    }

作用域中的前两个方法是声明别名cavtl1和tl1的其他作用域。<<是squeel中的not in操作符。

希望这能帮助到一些人。

最初的帖子特别提到了使用数字id,但我来这里寻找用字符串数组做NOT IN的语法。

ActiveRecord也会很好地为你处理:

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

要扩展@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让它变得更简单!