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?

184voto

Greg Campbell Punkte 15052

Es scheint, dass die :include Funktionalität wurde mit Rails 2.1 geändert. Früher führte Rails die Verknüpfung in allen Fällen durch, aber aus Leistungsgründen wurde sie geändert, um unter bestimmten Umständen mehrere Abfragen zu verwenden. Dieser Blogbeitrag von Fabio Akita enthält einige gute Informationen zu dieser Änderung (siehe den Abschnitt "Optimized Eager Loading").

122voto

Prem Punkte 5454

.joins verbindet nur die Tabellen und bringt ausgewählte Felder zurück. Wenn Sie Assoziationen auf das Ergebnis der Verbindungsabfrage aufrufen, werden erneut Datenbankabfragen ausgelöst

:includes lädt eifrig die enthaltenen Assoziationen und fügt sie im Speicher hinzu. :includes lädt alle Attribute der einbezogenen Tabellen. Wenn Sie Assoziationen für das Ergebnis einer Include-Abfrage aufrufen, werden keine Abfragen ausgelöst

80voto

holden Punkte 13281

Der Unterschied zwischen Joins und Includes besteht darin, dass mit der Include-Anweisung eine viel größere SQL-Abfrage erzeugt wird, die alle Attribute der anderen Tabelle(n) in den Speicher lädt.

Wenn Sie z.B. eine Tabelle voller Kommentare haben und ein :joins => users verwenden, um alle Benutzerinformationen für Sortierzwecke usw. zu erhalten, wird das gut funktionieren und weniger Zeit in Anspruch nehmen als :include, aber sagen wir, Sie wollen den Kommentar zusammen mit dem Namen des Benutzers, der E-Mail usw. anzeigen. Um die Informationen mit :joins zu erhalten, müssen für jeden Benutzer separate SQL-Abfragen durchgeführt werden, während bei der Verwendung von :include diese Informationen bereits zur Verfügung stehen.

Großartiges Beispiel:

http://railscasts.com/episodes/181-include-vs-joins

73voto

Aaditi Jain Punkte 6769

Ich habe kürzlich mehr über den Unterschied zwischen :joins y :includes in Schienen. Hier ist eine Erklärung dessen, was ich verstanden habe (mit Beispielen :))

Stellen Sie sich folgendes Szenario vor:

  • Ein Benutzer hat_viele Kommentare und ein Kommentar gehört_zu einem Benutzer.

  • Das Benutzermodell hat die folgenden Attribute: Name(string), Alter(integer). Das Modell Kommentar hat die folgenden Attribute: Inhalt, user_id. Für einen Kommentar kann die user_id null sein.

Tritt bei:

:joins führt eine innere Verknüpfung zwischen zwei Tabellen. So

Comment.joins(:user)

#=> <ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first   comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">, 
     #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,    
     #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">]>

holt alle Datensätze, bei denen user_id (aus der Tabelle comments) gleich user.id (Tabelle users) ist. Wenn Sie also

Comment.joins(:user).where("comments.user_id is null")

#=> <ActiveRecord::Relation []>

Sie erhalten ein leeres Array wie abgebildet.

Außerdem wird bei Joins die verbundene Tabelle nicht in den Speicher geladen. Wenn Sie also

comment_1 = Comment.joins(:user).first

comment_1.user.age
#=>[1m[36mUser Load (0.0ms)[0m  [1mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1[0m  [["id", 1]]
#=> 24

Wie Sie sehen, comment_1.user.age wird im Hintergrund erneut eine Datenbankabfrage durchgeführt, um die Ergebnisse zu erhalten

Enthält:

:includes führt eine linke äußere Verknüpfung zwischen den beiden Tabellen. Daher

Comment.includes(:user)

#=><ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">,
   #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,
   #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">,    
   #<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

führt zu eine verknüpfte Tabelle mit allen Datensätzen aus der Tabelle comments. Wenn Sie also

Comment.includes(:user).where("comment.user_id is null")
#=> #<ActiveRecord::Relation [#<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

werden Datensätze abgerufen, bei denen comments.user_id gleich null ist, wie gezeigt.

Außerdem werden beide Tabellen in den Speicher geladen. Wenn Sie also

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24

Wie Sie sehen können, lädt comment_1.user.age das Ergebnis einfach aus dem Speicher, ohne eine Datenbankabfrage im Hintergrund auszulösen.

57voto

Brian Maltzan Punkte 1307

Neben dem Leistungsaspekt gibt es auch einen funktionalen Unterschied. Wenn Sie Kommentare verknüpfen, fragen Sie nach Beiträgen, die Kommentare enthalten - standardmäßig eine innere Verknüpfung. Wenn Sie Kommentare einschließen, fragen Sie nach allen Beiträgen - eine äußere Verknüpfung.

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