Rails에서 관련 레코드가없는 레코드를 찾으려면
간단한 연관성을 고려하십시오 ...
class Person
has_many :friends
end
class Friend
belongs_to :person
end
ARel 및 / 또는 meta_where에 친구가없는 모든 사람을 얻는 가장 깨끗한 방법은 무엇입니까?
그리고 has_many : through 버전은 어떻습니까?
class Person
has_many :contacts
has_many :friends, :through => :contacts, :uniq => true
end
class Friend
has_many :contacts
has_many :people, :through => :contacts, :uniq => true
end
class Contact
belongs_to :friend
belongs_to :person
end
나는 정말로 counter_cache를 사용하고 싶지 않습니다. 그리고 읽은 것으로부터 has_many와는 작동하지 않습니다 :
모든 person.friends 레코드를 가져 와서 Ruby에서 반복하고 싶지 않습니다. meta_search gem과 함께 사용할 수있는 쿼리 / 범위를 갖고 싶습니다.
나는 쿼리의 성능 비용을 신경 쓰지 않는다
실제 SQL에서 멀수록 멀수록 좋습니다.
이것은 여전히 SQL과 매우 유사하지만 첫 번째 경우에는 친구가없는 모든 사람을 확보해야합니다.
Person.where('id NOT IN (SELECT DISTINCT(person_id) FROM friends)')
보다 나은:
Person.includes(:friends).where( :friends => { :person_id => nil } )
hmt의 경우 기본적으로 동일한 내용이므로 친구가없는 사람도 연락처가 없다는 사실에 의존합니다.
Person.includes(:contacts).where( :contacts => { :person_id => nil } )
최신 정보
has_one
의견 에 대한 질문이 있으므로 업데이트하십시오. 여기서 트릭 includes()
은 연결 이름은 필요하지만 where
테이블 이름은 필요합니다. A에 대한 has_one
연계는 일반적으로 변화하지만, 그래서, 단수로 표현 될 where()
부분 스테이는 그대로. 따라서 Person
유일한 has_one :contact
경우 귀하의 진술은 다음과 같습니다.
Person.includes(:contact).where( :contacts => { :person_id => nil } )
업데이트 2
누군가가없는 반대 친구에 대해 누군가가 물었습니다. 아래에서 주석을 달았을 때 실제로 마지막 필드 (위의 :person_id
)가 반환하는 모델과 실제로 관련 될 필요는 없으며 조인 테이블의 필드 일뿐입니다. 그것들은 모두 그들 중 하나가 될 수 nil
있도록 될 것입니다. 이것은 위의 간단한 솔루션으로 이어집니다.
Person.includes(:contacts).where( :contacts => { :id => nil } )
그리고 사람이없는 친구를 반환하기 위해 이것을 전환하면 훨씬 간단 해집니다. 앞면의 수업 만 변경하십시오.
Friend.includes(:contacts).where( :contacts => { :id => nil } )
업데이트 3-Rails 5
탁월한 Rails 5 솔루션에 대한 @Anson 덕분에 (아래 답변에 +1을 줄 수 있음) left_outer_joins
연결을로드하지 않도록 사용할 수 있습니다 .
Person.left_outer_joins(:contacts).where( contacts: { id: nil } )
사람들이 찾을 수 있도록 여기에 포함 시켰지만, +1을받을 자격이 있습니다. 훌륭한 추가!
smathy는 좋은 Rails 3 답변을 가지고 있습니다.
Rails 5의left_outer_joins
경우 연결로드를 피하기 위해 사용할 수 있습니다 .
Person.left_outer_joins(:contacts).where( contacts: { id: nil } )
API 문서를 확인하십시오 . 풀 요청 # 12071 에서 소개되었습니다 .
친구가없는 사람
Person.includes(:friends).where("friends.person_id IS NULL")
아니면 적어도 친구가 하나 있습니다
Person.includes(:friends).where("friends.person_id IS NOT NULL")
범위를 설정하여 Arel을 사용 하여이 작업을 수행 할 수 있습니다 Friend
class Friend
belongs_to :person
scope :to_somebody, ->{ where arel_table[:person_id].not_eq(nil) }
scope :to_nobody, ->{ where arel_table[:person_id].eq(nil) }
end
그리고 친구가 하나 이상있는 사람 :
Person.includes(:friends).merge(Friend.to_somebody)
친구없는 사람 :
Person.includes(:friends).merge(Friend.to_nobody)
Both the answers from dmarkow and Unixmonkey get me what I need - Thank You!
I tried both out in my real app and got timings for them - Here are the two scopes:
class Person
has_many :contacts
has_many :friends, :through => :contacts, :uniq => true
scope :without_friends_v1, -> { where("(select count(*) from contacts where person_id=people.id) = 0") }
scope :without_friends_v2, -> { where("id NOT IN (SELECT DISTINCT(person_id) FROM contacts)") }
end
Ran this with a real app - small table with ~700 'Person' records - average of 5 runs
Unixmonkey's approach (:without_friends_v1
) 813ms / query
dmarkow's approach (:without_friends_v2
) 891ms / query (~ 10% slower)
But then it occurred to me that I don't need the call to DISTINCT()...
I'm looking for Person
records with NO Contacts
- so they just need to be NOT IN
the list of contact person_ids
. So I tried this scope:
scope :without_friends_v3, -> { where("id NOT IN (SELECT person_id FROM contacts)") }
That gets the same result but with an average of 425 ms/call - nearly half the time...
Now you might need the DISTINCT
in other similar queries - but for my case this seems to work fine.
Thanks for your help
Unfortunately, you're probably looking at a solution involving SQL, but you could set it in a scope and then just use that scope:
class Person
has_many :contacts
has_many :friends, :through => :contacts, :uniq => true
scope :without_friends, where("(select count(*) from contacts where person_id=people.id) = 0")
end
Then to get them, you can just do Person.without_friends
, and you can also chain this with other Arel methods: Person.without_friends.order("name").limit(10)
A NOT EXISTS correlated subquery ought to be fast, particularly as the row count and ratio of child to parent records increases.
scope :without_friends, where("NOT EXISTS (SELECT null FROM contacts where contacts.person_id = people.id)")
Also, to filter out by one friend for instance:
Friend.where.not(id: other_friend.friends.pluck(:id))
참고URL : https://stackoverflow.com/questions/5319400/want-to-find-records-with-no-associated-records-in-rails
'IT story' 카테고리의 다른 글
Pandas Datetime 열과 별도로 월과 연도 추출 (0) | 2020.05.27 |
---|---|
기존 ENUM 유형에 새 값 추가 (0) | 2020.05.27 |
네비게이션 바에서 뒤로 버튼의 색상 변경 (0) | 2020.05.27 |
PHP에서 배열을 반향 또는 인쇄하는 방법은 무엇입니까? (0) | 2020.05.27 |
JQuery를 사용하여 Div에서 CSS 제거 (0) | 2020.05.27 |