ChatGPT解决这个技术问题 Extra ChatGPT

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

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

我发现 this article 引用了这个:

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>)
作为一个仅供参考,Datamapper 对 NOT IN 有特定的支持。示例:Person.all(:name.not => ['bob','rick','steve'])
抱歉我一无所知,但 Datamapper 是什么?那是rails 3的一部分吗?
数据映射器是另一种存储数据的方式,它用不同的结构替换 Active Record,然后你以不同的方式编写与模型相关的东西,例如查询。

n
notapatch

导轨 4+:

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

导轨 3:

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

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


这是使用最新 Active Record 查询模型的正确方法
@NewAlexandria 是对的,因此您必须执行 Topic.where('id NOT IN (?)', (actions.empty? ? '', actions) 之类的操作。它仍然会在 nil 上中断,但我发现您传入的数组通常是由一个过滤器生成的,该过滤器至少会返回 [],而且永远不会返回 nil。我建议查看 Squeel,它是 Active Record 之上的 DSL。然后你可以这样做:Topic.where{id.not_in actions},nil/empty/或其他。
@danneu 只需将 .empty? 换成 .blank? 就可以了
使用 rails 4 表示法: Article.where.not(title: ['Rails 3', 'Rails 5'])
正如@NewAlexandria 所说,当变量为 []nil 时,这不起作用。您最终得到 id NOT in (null)。要解决这个问题,请使用 presence,如下所示:Topic.where( 'id NOT IN (?)', actions.presence || "" )
T
Trung Lê

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

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

最后!他们花了这么长时间才把它包括在内? :)
P
Pedro Rolo

使用阿雷尔:

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)

并且由于rails 4在:

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

请注意,最终您不希望 forum_ids 成为 ids 列表,而是一个子查询,如果是这样,那么您应该在获取主题之前执行以下操作:

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

另请注意,最终您不想这样做,而是加入 - 可能更有效。


连接可能更有效,但不一定。请务必使用 EXPLAIN
A
Andrew Marshall

您可以尝试以下方法:

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)

V
Vincent Cadoret

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


A
Andrew Marshall

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

A
Andrew Marshall

上面的大多数答案应该足以满足您的需求,但如果您要做更多此类谓词和复杂组合,请查看 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)}

A
Andrew Marshall

您可能想看看 Ernie Miller 的 meta_where plugin。您的 SQL 语句:

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

...可以这样表达:

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

Railscasts 的 Ryan Bates 创建了一个 nice screencast explaining MetaWhere

不确定这是否是您要查找的内容,但在我看来,它肯定比嵌入式 SQL 查询更好。


A
Andy Triggs

原始帖子特别提到了使用数字 ID,但我来这里是为了寻找用字符串数组执行 NOT IN 的语法。

ActiveRecord 也会很好地为您处理:

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

O
Omar Qureshi

这些论坛 id 能否以务实的方式制定?例如,您能以某种方式找到这些论坛吗?如果是这种情况,您应该做类似的事情

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

这比执行 SQL not in 更有效


e
evanrmurphy

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

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

t
tjeden

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

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

C
Chuck Callebs

搭载 jonnii:

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

使用 pluck 而不是映射元素

通过 railsconf 2012 10 things you did not know rails could do 找到


C
CRABOLO

当您查询一个空白数组时,将“<< 0”添加到 where 块中的数组中,这样它就不会返回“NULL”并中断查询。

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

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


警告:这实际上在数组中添加了一个 0,因此它不再为空。它还具有修改数组的副作用——如果你以后使用它,就会有双重危险。最好将其包装在 if-else 中并使用 Topic.none/all 处理边缘情况
更安全的方法是:Topic.where("id NOT IN (?)", actions.presence || [0])
d
dukha

这是一个更复杂的“不在”查询,在使用 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 运算符。

希望这可以帮助某人。


L
LEONARDO PEREIRA RODRIGUES

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

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

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