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

发布于 2024-10-05 19:15:30 字数 522 浏览 0 评论 0原文

我希望有一个不涉及 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>)

I'm hoping there is a easy solution that doesn't involve find_by_sql, if not then I guess that will have to work.

I found this article which references this:

Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })

which is the same as

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

I am wondering if there is a way to do NOT IN with that, like:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

森末i 2024-10-12 19:15:30

Rails 4+:

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

Rails 3:

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 3:

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

Where actions is an array with: [1,2,3,4,5]

多孤肩上扛 2024-10-12 19:15:30

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

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

FYI, In Rails 4, you can use not syntax:

Article.where.not(title: ['Rails 3', 'Rails 5'])
幸福%小乖 2024-10-12 19:15:30

从rails 4开始:

Topic.where.not(forum_id: @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*/)

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

since rails 4:

Topic.where.not(forum_id: @forum_ids)

Please notice that eventually you do not want the forum_ids to be the ids list, but rather a subquery, if so then you should do something like this before getting the topics:

@forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)

in this way you get everything in a single query: something like:

select * from topic 
where forum_id in (select id 
                   from forum 
                   where /*whatever conditions are desirable*/)

Also notice that eventually you do not want to do this, but rather a join - what might be more efficient.

尘曦 2024-10-12 19:15:30

您可以尝试以下操作:

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)

You can try something like:

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

You might need to do @forums.map(&:id).join(','). I can't remember if Rails will the argument into a CSV list if it is enumerable.

You could also do this:

# 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)
烂柯人 2024-10-12 19:15:30

要扩展 @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 使这一切变得更加容易!

To expand on @Trung Lê answer, in Rails 4 you can do the following:

Topic.where.not(forum_id:@forums.map(&:id))

And you could take it a step further.
If you need to first filter for only published Topics and then filter out the ids you don't want, you could do this:

Topic.where(published:true).where.not(forum_id:@forums.map(&:id))

Rails 4 makes it so much easier!

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

The accepted solution fails if @forums is empty. To workaround this I had to do

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

Or, if using Rails 3+:

Topic.where( 'forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id)) ).all
无言温柔 2024-10-12 19:15:30

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

Most of the answers above should suffice you but if you are doing a lot more of such predicate and complex combinations check out Squeel. You will be able to doing something like:

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)}
时光清浅 2024-10-12 19:15:30

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

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

,这比执行 SQL not in 更有效

Can these forum ids be worked out in a pragmatic way? e.g. can you find these forums somehow - if that is the case you should do something like

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

Which would be more efficient than doing an SQL not in

枕梦 2024-10-12 19:15:30

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

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

This way optimizes for readability, but it's not as efficient in terms of database queries:

# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - @forums.map(&:id)
霓裳挽歌倾城醉 2024-10-12 19:15:30

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

You may want to have a look at the meta_where plugin by Ernie Miller. Your SQL statement:

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

...could be expressed like this:

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

Ryan Bates of Railscasts created a nice screencast explaining MetaWhere.

Not sure if this is what you're looking for but to my eyes it certainly looks better than an embedded SQL query.

瞳孔里扚悲伤 2024-10-12 19:15:30

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

ActiveRecord 也会很好地为你处理这个问题:

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

The original post specifically mentions using numeric IDs, but I came here looking for the syntax for doing a NOT IN with an array of strings.

ActiveRecord will handle that nicely for you too:

Thing.where(['state NOT IN (?)', %w{state1 state2}])
一腔孤↑勇 2024-10-12 19:15:30

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

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

You can use sql in your conditions:

Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)])
凉风有信 2024-10-12 19:15:30

搭载 jonnii:

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

元素

使用 pluck 而不是映射通过 railsconf 2012 你不知道 Rails 可以做的 10 件事

Piggybacking off of jonnii:

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

using pluck rather than mapping over the elements

found via railsconf 2012 10 things you did not know rails could do

尝蛊 2024-10-12 19:15:30

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

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

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

When you query a blank array add "<< 0" to the array in the where block so it doesn't return "NULL" and break the query.

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

If actions could be an empty or blank array.

怼怹恏 2024-10-12 19:15:30

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

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

希望这对某人有帮助。

Here is a more complex "not in" query, using a subquery in rails 4 using squeel. Of course very slow compared to the equivalent sql, but hey, it works.

    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)}
    }

The first 2 methods in the scope are other scopes which declare the aliases cavtl1 and tl1. << is the not in operator in squeel.

Hope this helps someone.

最笨的告白 2024-10-12 19:15:30

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

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

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

If someone want to use two or more conditions, you can do that:

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文