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


207

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

3
참고로, Datamapper는 NOT IN을 구체적으로 지원했습니다. 예 :Person.all(:name.not => ['bob','rick','steve'])
Mark Thomas

1
무지해서 죄송하지만 Datamapper 란 무엇입니까? 레일 3의 일부입니까?
Toby Joiner

2
데이터 매퍼는 데이터를 저장하는 다른 방법으로, Active Record를 다른 구조로 대체 한 다음 쿼리와 같은 모델 관련 항목을 다르게 작성합니다.
Michael Durrant

답변:


313

레일 4+ :

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

레일 3 :

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

다음 actions과 같은 배열은 어디에 있습니까?[1,2,3,4,5]


1
이 최신 액티브 레코드 쿼리 모델과 적절한 접근 방법이다
Nevir

5
@NewAlexandria가 옳으므로 다음과 같은 작업을 수행해야합니다 Topic.where('id NOT IN (?)', (actions.empty? ? '', actions). 여전히 nil에서 끊어 지지만 전달하는 배열은 일반적으로 []최소한 반환 되며 결코 nil 이 아닌 필터에 의해 생성됩니다 . Active Record 위에있는 DSL 인 Squeel을 확인하는 것이 좋습니다. 그런 다음 Topic.where{id.not_in actions}, nil / empty / 또는 그렇지 않으면 할 수 있습니다.
danneu

6
@danneu 단지 스왑 .empty?에 대한 .blank?당신은 무풍 증거
colllin

(actions.empty ''행동 @daaneu이어야에 의해 (actions.empty????) ':'행동)
마르셀 salathe

3
rails 4 표기법으로 이동 : Article.where.not (title : [ 'Rails 3', 'Rails 5']]
Tal

152

참고로 Rails 4에서는 다음 not구문 을 사용할 수 있습니다 .

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

11
드디어! 그것들을 포함시키기 위해 무엇이 그렇게 오래 걸렸습니까? :)
Dominik Goltermann

50

다음과 같은 것을 시도해 볼 수 있습니다.

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)

50

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

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


2
조인 더 효율적일 있지만 반드시 그런 것은 아닙니다. 사용하십시오 EXPLAIN!
James

20

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


12

@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

4

위의 답변의 대부분은 당신에게 충분하지만, 당신이 많은 술어와 복잡한 조합을 많이하고 있다면 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)}

2

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 쿼리보다 더 좋아 보입니다.


2

원래 게시물에는 특히 숫자 ID 사용에 대해 언급되어 있지만 문자열 배열로 NOT IN을 수행하는 구문을 찾고 있습니다.

ActiveRecord도 당신을 위해 그것을 잘 처리 할 것입니다 :

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

1

이 포럼 ID를 실용적으로 해결할 수 있습니까? 예를 들어이 포럼을 어떻게 든 찾을 수 있습니까?

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

SQL을 수행하는 것보다 더 효율적인 방법 not in


1

이 방법은 가독성을 최적화하지만 데이터베이스 쿼리 측면에서 비효율적입니다.

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

0

조건에 따라 sql을 사용할 수 있습니다.

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

0

jonnii의 피기 백 :

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

요소를 매핑하는 대신 pluck 사용

railsconf 2012 를 통해 찾았습니다.


0

빈 배열을 쿼리 할 때는 "<< 0"을 where 블록의 배열에 추가하여 "NULL"을 반환하지 않고 쿼리를 중단합니다.

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

조치가 비어 있거나 비어있는 배열 일 수있는 경우


1
경고 : 실제로 배열에 0을 추가하므로 더 이상 비어 있지 않습니다. 또한 배열을 수정하면 부작용이 발생합니다. 나중에 사용하면 위험이 두 배가됩니다. if-else로 감싸서 Topic.none / all을 가장자리 케이스에 사용하는 것이 훨씬 좋습니다.
Ted Pennings

더 안전한 방법은 다음과 같습니다Topic.where("id NOT IN (?)", actions.presence || [0])
웨스턴 GANGER

0

다음은 squeel을 사용하는 레일 4의 하위 쿼리를 사용하는보다 복잡한 "not in"쿼리입니다. 물론 동등한 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)}
    }

범위의 처음 두 가지 방법은 별칭 cavtl1 및 tl1을 선언하는 다른 범위입니다. <<는 squeel에서 not 연산자입니다.

이것이 누군가를 돕기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.