Jest to raczej pytanie „dlaczego to działa w ten sposób”, a nie pytanie „nie wiem jak to zrobić” ...
Dlatego ewangelią związaną z :include
pobieraniem powiązanych rekordów, z których wiesz, że będziesz korzystać, jest skorzystanie, ponieważ dostaniesz połączenie i unikniesz mnóstwa dodatkowych zapytań:
Post.all(:include => :comments)
Jednak gdy spojrzysz na dzienniki, nie nastąpi żadne połączenie:
Post Load (3.7ms) SELECT * FROM "posts"
Comment Load (0.2ms) SELECT "comments.*" FROM "comments"
WHERE ("comments".post_id IN (1,2,3,4))
ORDER BY created_at asc)
To jest na skróty, ponieważ ciągnie wszystkie komentarze na raz, ale to nie jest jeszcze dołączyć (czyli to, co wydaje się cała dokumentacja powiedzieć). Jedynym sposobem na uzyskanie sprzężenia jest użycie :joins
zamiast :include
:
Post.all(:joins => :comments)
A dzienniki pokazują:
Post Load (6.0ms) SELECT "posts".* FROM "posts"
INNER JOIN "comments" ON "posts".id = "comments".post_id
Czy coś brakuje? Mam aplikację z pół tuzinem skojarzeń i na jednym ekranie wyświetlam dane ze wszystkich. Wydaje się, że lepiej byłoby mieć jedno zapytanie łączone zamiast 6 osób. Wiem, że pod względem wydajności nie zawsze lepiej jest łączyć zamiast pojedynczych zapytań (w rzeczywistości jeśli spędzasz czas, wygląda na to, że dwa powyższe zapytania są szybsze niż łączenie), ale po wszystkich dokumentach Czytam, jestem zaskoczony, widząc, że :include
nie działa zgodnie z reklamą.
Może Railsy są świadome problemu z wydajnością i nie dołączają, z wyjątkiem niektórych przypadków?
źródło
includes
(dla każdego, kto to czyta)Odpowiedzi:
Wygląda na to, że
:include
funkcjonalność została zmieniona w Rails 2.1. Szyny zawsze wykonywały łączenie we wszystkich przypadkach, ale ze względu na wydajność zmieniono tak, aby w niektórych okolicznościach korzystało z wielu zapytań. Ten post na blogu autorstwa Fabio Akita zawiera kilka dobrych informacji na temat zmiany (zobacz sekcję „Zoptymalizowane szybkie ładowanie”).źródło
.joins
po prostu dołącza do tabel i zwraca wybrane pola w zamian. jeśli wywołasz asocjacje przy wyniku zapytania o przyłączenie, ponownie uruchomi zapytania do bazy danych:includes
chętnie załaduje dołączone skojarzenia i doda je do pamięci.:includes
ładuje wszystkie zawarte atrybuty tabel. Jeśli wywołasz skojarzenia przy włączonym wyniku zapytania, nie będzie on uruchamiał żadnych zapytańźródło
Różnica między łączeniami i włączaniem polega na tym, że użycie instrukcji włącza generuje znacznie większe zapytanie SQL ładujące do pamięci wszystkie atrybuty z innych tabel.
Na przykład, jeśli masz tabelę pełną komentarzy i używasz: joins => users, aby pobrać wszystkie informacje o użytkowniku do celów sortowania itp., To będzie działać poprawnie i zajmie mniej czasu niż: include, ale powiedz, że chcesz wyświetlić komentarz wraz z nazwą użytkownika, adresem e-mail itp. Aby uzyskać informacje za pomocą: łączy, będzie musiał utworzyć osobne zapytania SQL dla każdego pobieranego użytkownika, a jeśli użyłeś: dołącz tę informację, jest gotowa do użycia.
Świetny przykład:
http://railscasts.com/episodes/181-include-vs-joins
źródło
Byłem niedawno czytać więcej na różnicy pomiędzy
:joins
i:includes
na szynach. Oto wyjaśnienie tego, co zrozumiałem (z przykładami :))Rozważ ten scenariusz:
Użytkownik ma wiele komentarzy, a komentarz należy do użytkownika.
Model użytkownika ma następujące atrybuty: Nazwa (ciąg), Wiek (liczba całkowita). Model komentarza ma następujące atrybuty: Content, user_id. W przypadku komentarza identyfikator_użytkownika może mieć wartość NULL.
Dołącza:
: joins wykonuje wewnętrzne połączenie między dwiema tabelami. A zatem
pobierze wszystkie rekordy, w których user_id (tabeli komentarzy) jest równy user.id (tabeli użytkowników). Zatem jeśli tak
Otrzymasz pustą tablicę, jak pokazano.
Ponadto sprzężenia nie ładują połączonej tabeli do pamięci. Zatem jeśli tak
Jak widać,
comment_1.user.age
ponownie uruchomi zapytanie bazy danych w tle, aby uzyskać wynikiObejmuje:
: obejmuje wykonuje lewe połączenie zewnętrzne między dwiema tabelami. A zatem
spowoduje połączenie tabeli ze wszystkimi rekordami z tabeli komentarzy. Zatem jeśli tak
pobierze rekordy, w których comments.user_id ma wartość zero, jak pokazano.
Ponadto zawiera obciążenia obu tabel w pamięci. Zatem jeśli tak
Jak widać, komentarz_1.user.age po prostu ładuje wynik z pamięci bez uruchamiania zapytania bazy danych w tle.
źródło
Oprócz względów wydajnościowych istnieje również funkcjonalna różnica. Kiedy dołączasz do komentarzy, pytasz o posty z komentarzami - domyślnie jest to połączenie wewnętrzne. Gdy dołączysz komentarze, poprosisz o wszystkie posty - dołączenie zewnętrzne.
źródło
tl; dr
Kontrastuję je na dwa sposoby:
złączenia - do warunkowego wyboru rekordów.
obejmuje - podczas korzystania z powiązania na każdym elemencie zestawu wyników.
Dłuższa wersja
Połączenia służą do filtrowania zestawu wyników pochodzących z bazy danych. Używasz go do wykonywania operacji ustawiania na stole. Pomyśl o tym jak o klauzuli where, która wykonuje zbiór teorii.
Post.joins(:comments)
jest taki sam jak
Post.where('id in (select post_id from comments)')
Tyle że jeśli jest więcej niż jeden komentarz, otrzymasz z powrotem duplikaty postów wraz z dołączeniami. Ale każdy post będzie postem z komentarzami. Możesz to poprawić, wyróżniając:
W umowie
includes
metoda po prostu upewni się, że nie ma żadnych dodatkowych zapytań do bazy danych podczas odwoływania się do relacji (abyśmy nie zadawali n + 1 zapytań)Morał polega na tym, aby używać,
joins
gdy chcesz wykonywać operacje zestawu warunkowego, i używać,includes
gdy zamierzasz używać relacji na każdym elemencie kolekcji.źródło
distinct
mnie łapie za każdym razem. Dziękuję Ci!.joins działa jako połączenie z bazą danych i łączy dwie lub więcej tabel i pobiera wybrane dane z zaplecza (bazy danych).
. obejmuje pracę jako lewe przyłączenie bazy danych. Załadował wszystkie rekordy lewej strony, nie ma znaczenia modelu po prawej stronie. Służy do szybkiego ładowania, ponieważ ładuje wszystkie skojarzone obiekty w pamięci. Jeśli wywołujemy skojarzenia przy uwzględnieniu wyniku zapytania, to nie uruchamia zapytania w bazie danych, po prostu zwraca dane z pamięci, ponieważ już załadowało dane do pamięci.
źródło
„złączenia” służyły tylko do łączenia tabel, a gdy wywoływano skojarzenia przy złączeniach, ponownie uruchomi zapytanie (oznacza to, że wiele zapytań zostanie uruchomionych)
w tym przypadku całkowita liczba SQL wynosi 11
Ale z „włączeniem” chętnie załaduje dołączone skojarzenia i doda je do pamięci (załaduje wszystkie skojarzenia przy pierwszym załadowaniu) i nie uruchomi ponownie zapytania
gdy otrzymasz rekordy zawierające między innymi @ @ records = User.includes (: organizacje) .where („organisations.user_id = 1”), wówczas zapytanie będzie
@ records.map {| u | u.organisation.name} żadne zapytanie nie zostanie uruchomione
źródło