ruby on rails validates 如何用ActiveRecord/Rails表达NOT IN查询?




ruby rails介绍 (12)

只是为了更新这个,因为看起来很多人都这样认为,如果您使用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>)

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

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

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

ActiveRecord也会为你处理:

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


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


上面的大部分答案都可以满足你,但如果你正在做更多这样的谓词和复杂组合,请检查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)}

如果@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

这些论坛ID可以用实用的方式解决吗? 例如,你能否以某种方式找到这些论坛 - 如果是这样的话,你应该这样做

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

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


要扩展@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使它更容易!


我正在使用这个:

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

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

编辑:

对于Rails 4表示法:

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

使用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*/)

另外请注意,最终你不希望这样做,而是加入 - 可能更有效。


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

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

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


你可以尝试如下所示:

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)

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

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




activerecord