如何用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运算符。
希望这有助于某人。