Programming

ActiveRecord / Rails로 NOT IN 쿼리를 표현하는 방법?

procodes 2020. 5. 10. 11:54
반응형

ActiveRecord / Rails로 NOT IN 쿼리를 표현하는 방법?


Rails 4를 사용하고 있다면 Trung Lê`와 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>)

그와 관련이있는 방법이 있는지 궁금합니다 NOT IN.

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

나는 이것을 사용하고있다 :

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

또한 결국이 작업을 원하지 않고 오히려 조인이 더 효율적일 수 있습니다.


@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는 훨씬 쉬워졌습니다!


@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

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

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.


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

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


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)

You can use sql in your conditions:

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

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


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.


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.

참고URL : https://stackoverflow.com/questions/4307411/how-to-express-a-not-in-query-with-activerecord-rails

반응형