2010-11-12 9 views
16

Czy ktoś może to wyjaśnić?szyny wybierz i włącz

Spowoduje to wykonanie 3 zapytań, jednego do pobrania projektów, jednego do pobrania użytkowników do tych projektów i jednego do pobrania firm.

Project.select("name").includes([:user, :company]) 

Wykonuje 3 zapytania i całkowicie ignoruje bit wyboru.

Project.select("user.name").includes([:user, :company]) 

Spowoduje to wykonanie 1 zapytania z właściwymi lewymi złączeniami. I wciąż całkowicie ignoruje wybór.

Wydaje mi się, że szyny ignorują wybór przy uwzględnieniu. No dobra, ale dlaczego, kiedy wstawiam pokrewny model w selekcji, zmienia się on z wydawania 3 zapytań na wydawanie 1 zapytania?

Zauważ, że jedno zapytanie jest tym, czego chcę, po prostu nie mogę sobie wyobrazić, że jest to właściwy sposób, aby go uzyskać, ani dlaczego działa, ale nie jestem pewien, jak inaczej uzyskać wyniki w jednym zapytaniu (. joins wydaje się używać INNER JOIN, którego tak naprawdę nie chcę, a kiedy ręcznie sprecyzuję warunki łączenia do .joins w gemie wyszukiwania, używamy maniaków, gdy próbuje on ponownie dodać sprzężenia o tej samej nazwie).

+0

możliwy duplikat [Rails 3 - wybierz z włączeniem?] (Http://stackoverflow.com/questions/4047833/rails-3-select-with-include) –

Odpowiedz

8

Allright tak oto co wymyśliłem ...

.joins("LEFT JOIN companies companies2 ON companies2.id = projects.company_id LEFT JOIN project_types project_types2 ON project_types2.id = projects.project_type_id LEFT JOIN users users2 ON users2.id = projects.user_id") \ 
.select("six, fields, I, want") 

Works, upierdliwe, ale robi mi tylko do potrzebnych danych w jednym zapytaniu. Jedyną kiepską częścią jest to, że muszę podać wszystko jako alias modelu2, ponieważ używamy meta_search, który wydaje się nie być w stanie odgadnąć, że tabela jest już połączona po określeniu własnych warunków łączenia.

+0

Wiem, że jest nieprecyzyjny, ale poprawna odpowiedź to "Nie przesadzaj z optymalizacją". 99% czasu po prostu pozwól mu to zrobić. – Kevin

0

Możliwe, że czegoś tutaj brakuje, ale select i include nie są częścią ActiveRecord. Zwykłym sposobem zrobić to, co starasz się zrobić to tak:

Project.find(:all, :select => "users.name", :include => [:user, :company], :joins => "LEFT JOIN users on projects.user_id = users.id") 

Spójrz na api documentation więcej przykładów. Od czasu do czasu miałem iść obsługi i korzystania find_by_sql:

Project.find_by_sql("select users.name from projects left join users on projects.user_id = users.id") 

Mam nadzieję, że będzie to punkt, który w dobrym kierunku.

+2

select i include są częścią interfejsu zapytań Rails 3 http://guides.rubyonrails.org/active_record_querying.html – rwilliams

+0

Pomyślałem, że mogło tak być (mam jeszcze zagłębić się w v3), chociaż nie mogłem znaleźć odniesienia do nich w dokumentacji API. Składnia wydaje się trochę sprzeczna z intuicją, wynikającą z tego, co widzę. –

2

Szyny zawsze ignorowane select argumentu (ów) przy użyciu include lub includes. Jeśli chcesz użyć argumentu select, użyj zamiast tego joins.

Być może problem dotyczy kwitu zapytania, o którym mówisz, ale możesz także dołączyć fragmenty sql za pomocą metody łączenia.

Project.select("name").joins(['some sql fragement for users', 'left join companies c on c.id = projects.company_id']) 

Nie znam twojego schematu, więc musiałbym zgadywać na temat dokładnych relacji, ale to powinno zacząć.

+1

Właściwie moje pytanie było bardziej zgodne z linią, jeśli wybór jest ignorowany, dlaczego zmienia sposób uruchamiania kwerendy? W szczególności, przy użyciu opcji włącz bez wyboru, wydano wiele zapytań, np. Model poczty i jeden dla każdego skojarzenia. Jednak przy wybranym dodaniu dla wszystkich danych uruchamiane jest tylko jedno zapytanie. Powodem, dla którego o to pytam, jest to, że dla wydajności chcę wykonać tylko jedno zapytanie dla tych danych. I staram się unikać złączeń, ponieważ używają one wewnętrznego sprzężenia, lub jeśli określę lewe sprzężenie, wtedy powodują problemy z klejnotem meta_search, gdy spróbują ponownie dodać sprzężenie. – Chad

+0

Który gem wyszukiwania używasz? – rwilliams

+0

meta_search, zobacz poniżej rozwiązanie, które mam na razie ... – Chad

25

Miałem ten sam problem z wyborem i obejmuje. W celu szybkiego załadowania skojarzonych modeli użyłem natywnego zakresu Rails 'preload' http://apidock.com/rails/ActiveRecord/QueryMethods/preload Zapewnia on duże obciążenie bez przeskakiwania "select" w łańcuchu zakresów.

znalazłem go tutaj https://github.com/rails/rails/pull/2303#issuecomment-3889821

nadzieję, że ta końcówka będzie pomocny dla kogoś, jak to było pomocne dla mnie.

+3

To jest prawdziwa odpowiedź. ': preload' FTW –

0

Ja chciałem tej funkcjonalności samodzielnie, więc proszę go używać. Uwzględnij tę metodę w swojej klasie

#ACCEPTS args w formacie string "ASSOCIATION_NAME: COLUMN_NAME-COLUMN_NAME"

def self.includes_with_select(*m) 
    association_arr = [] 
    m.each do |part| 
     parts = part.split(':') 
     association = parts[0].to_sym 
     select_columns = parts[1].split('-') 
     association_macro = (self.reflect_on_association(association).macro) 
     association_arr << association.to_sym 
     class_name = self.reflect_on_association(association).class_name 
     self.send(association_macro, association, -> {select *select_columns}, class_name: "#{class_name.to_sym}") 
    end 
    self.includes(*association_arr) 
    end 

I będziesz w stanie wywołać tak: Contract.includes_with_select ('user id-name -status ',' confirmation: confirmed-id '), i wybierze te określone kolumny.