2013-03-23 9 views
6

Załóżmy, że mamy "Temat - Związek - Kategoria".Jak znaleźć rekordy bez powiązanych rekordów w has_many poprzez powiązanie z Active Record?

Oznacza to, że Temat has_many categories through relationship.

myślę, że bardzo łatwo dostać się na tematy, które z kategorii

#Relationship Model 
    Topic_id: integer 
    Category_id: integer 

    @topics=Topic.joins(:relationships) 

ale nie każdy temat ma kategorię. Jak więc odzyskać temat, który nie ma kategorii? Czy istnieje zapytanie minus?

Być może wygląda to na @topics=Topic.where('id NOT IN (?)', Relationship.all) Znajduję to w activerecord equivalent to SQL 'minus', ale nie jestem pewien co do tego rozwiązania.

+1

można nie uzyskać listę przez coś podobnego 'Topic.where (?: Categories.nil)' –

+0

AR zwraca pustą tablicę, gdy nic nie zostanie znaleziony w związku nie zerowej. – holaSenor

+0

@ tester123, okrzyki za wyjaśnienia, używam Datamapper przez pewien czas. –

Odpowiedz

10

Byłoby lepiej jako relacja, naprawdę. Że to będzie działać:

@topics = Topic.joins('left join relationships on relationships.topic_id = topics.id').where('relationships.category_id is null') 

albo to:

@topics = Topic 
    .joins('left join relationships on relationships.topic_id = topics.id join categories on categories.id = relationships.category_id') 
    .group('topics.id').having('count(categories.id) = 0') 
+0

bingo, prawidłowa odpowiedź to użycie lewego połączenia –

0

Spróbuj tego, wybiera tylko temat, w którym kategorie Obiekt relacji ma długość zero.

@topics = Topic.all.select {|t| t.categories.length == 0 } 
+0

Odpowiedź MrTheWalrus jest prawdopodobnie lepsza, w zależności od tego, ile tematów jest obecnych w tabeli, a oczekiwany wzrost może nie mieć znaczenia, jeśli wykonuje tylko niektóre kontrole ad hoc, ale jeśli to się często nazywa, powinieneś przetestować wszystkie 3 opcje i wybrać najbardziej efektywnie korzystającą z bazy danych. – holaSenor

+2

To jest złe rozwiązanie. Zamierza wciągnąć wszystkie tematy do pamięci i przeszukać bazę danych oddzielnie dla każdego. Można to zrobić jako pojedyncze zapytanie, a la rozwiązanie Mr TheWalrus. Zdecydowana większość czasu, każda logika, która może zostać zaimplementowana w bazie danych, powinna zostać zaimplementowana w bazie danych. Jest znacznie szybszy i znacznie wydajniejszy pod względem pamięci i CPU. – eirikir

+1

Ouch. Tak, wyobraźcie sobie, że gdybyśmy mieli milion tematów !, chciałbym skorzystać z sugestii @MrTheWalrus. – holaSenor

0

Szukałem Najprostsza odpowiedź, co moim zdaniem jest użycie includes.

topics = Topic.includes(:relationships).where(relationships: {id: nil})

Innym sposobem, który jest bardziej poprawna i dostaje myślisz SQL jest LEFT OUTER JOINS.

Topic.joins("LEFT OUTER JOINS relationships ON relationships.topic_id = topics.id") 
    .where(relationships: {id: nil}) 
Powiązane problemy