375 Stimmen

Rails :include vs. :joins

Dies ist eher eine "Warum funktionieren die Dinge so?"-Frage als eine "Ich weiß nicht, wie man das macht"-Frage...

Das Evangelium über das Abrufen von verknüpften Datensätzen, von denen Sie wissen, dass Sie sie verwenden werden, lautet also :include denn Sie erhalten eine Verknüpfung und vermeiden eine ganze Reihe von zusätzlichen Abfragen:

Post.all(:include => :comments)

Wenn Sie sich jedoch die Protokolle ansehen, findet keine Verbindung statt:

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) 

Es est ist eine Abkürzung, weil sie alle Kommentare auf einmal zieht, aber es ist immer noch keine Verknüpfung (was die gesamte Dokumentation zu sagen scheint). Der einzige Weg, wie ich eine Verknüpfung erhalten kann, ist die Verwendung von :joins anstelle von :include :

Post.all(:joins => :comments)

Und die Protokolle zeigen:

Post Load (6.0ms)  SELECT "posts".* FROM "posts" 
                   INNER JOIN "comments" ON "posts".id = "comments".post_id

Habe ich etwas verpasst? Ich habe eine App mit einem halben Dutzend Assoziationen, und auf einem Bildschirm zeige ich Daten von allen an. Es scheint, als ob es besser wäre, eine einzige verknüpfte Abfrage zu haben, anstatt 6 einzelne. Ich weiß, dass es von der Leistung her nicht immer besser ist, eine Verknüpfung statt einzelner Abfragen durchzuführen (wenn man nach der verbrauchten Zeit geht, sieht es so aus, als ob die beiden obigen einzelnen Abfragen schneller sind als die Verknüpfung), aber nach all den Dokumenten, die ich gelesen habe, bin ich überrascht zu sehen :include nicht wie angekündigt funktioniert.

Vielleicht Rails est sich des Leistungsproblems bewusst ist und nur in bestimmten Fällen beitritt?

15voto

Kevin Choubacha Punkte 358

tl;dr

Ich stelle sie in zweierlei Hinsicht gegenüber:

tritt bei. - Für die bedingte Auswahl von Datensätzen.

enthält - Bei Verwendung einer Assoziation für jedes Mitglied einer Ergebnismenge.

Längere Version

Joins ist dazu gedacht, die aus der Datenbank stammende Ergebnismenge zu filtern. Sie verwenden es, um Mengenoperationen mit Ihrer Tabelle durchzuführen. Stellen Sie sich dies als eine Where-Klausel vor, die die Mengenlehre durchführt.

Post.joins(:comments)

ist dasselbe wie

Post.where('id in (select post_id from comments)')

Wenn Sie jedoch mehr als einen Kommentar abgeben, erhalten Sie doppelte Beiträge mit den Verknüpfungen zurück. Aber jeder Beitrag wird ein Beitrag sein, der Kommentare hat. Sie können dies mit distinct korrigieren:

Post.joins(:comments).count
=> 10
Post.joins(:comments).distinct.count
=> 2

Im Vertrag ist die includes Methode stellt einfach sicher, dass es keine zusätzlichen Datenbankabfragen gibt, wenn die Beziehung referenziert wird (so dass wir nicht n + 1 Abfragen machen)

Post.includes(:comments).count
=> 4 # includes posts without comments so the count might be higher.

Die Moral ist, dass man joins wenn Sie bedingte Mengenoperationen durchführen wollen und die includes wenn Sie für jedes Mitglied einer Sammlung eine Beziehung verwenden wollen.

4voto

.joins arbeitet als Datenbank Join und es verbindet zwei oder mehr Tabellen und holt ausgewählte Daten aus dem Backend (Datenbank).

.includes arbeiten als Left Join der Datenbank. Es lädt alle Datensätze der linken Seite, hat keine Relevanz der rechten Seite Modell. Es wird zum eifrigen Laden verwendet, weil es alle assoziierten Objekte in den Speicher lädt. Wenn wir Assoziationen auf Include-Abfrageergebnis aufrufen, dann feuert es keine Abfrage an die Datenbank, sondern gibt einfach Daten aus dem Speicher zurück, weil es bereits Daten im Speicher geladen hat.

0voto

Thorin Punkte 1946

'joins' nur verwendet, um Tabellen zu verbinden und wenn Sie Assoziationen auf joins dann wird es wieder Feuer Abfrage (es bedeutet, viele Abfrage wird Feuer)

lets suppose you have tow model, User and Organisation
User has_many organisations
suppose you have 10 organisation for a user 
@records= User.joins(:organisations).where("organisations.user_id = 1")
QUERY will be 
 select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1

it will return all records of organisation related to user
and @records.map{|u|u.organisation.name}
it run QUERY like 
select * from organisations where organisations.id = x then time(hwo many organisation you have)

Die Gesamtzahl von SQL beträgt in diesem Fall 11.

Aber mit includes' werden die enthaltenen Assoziationen eifrig geladen und im Speicher hinzugefügt (alle Assoziationen werden beim ersten Laden geladen) und die Abfrage wird nicht erneut ausgelöst

wenn Sie Datensätze mit Einträgen wie @records= User.includes(:organisations).where("organisations.user_id = 1") dann wird die Abfrage

select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1
and 

 select * from organisations where organisations.id IN(IDS of organisation(1, to 10)) if 10 organisation
and when you run this 

@records.map{|u|u.organisation.name} keine Abfrage wird ausgelöst

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X