LEFT OUTER schließt sich Rails 3 an

85

Ich habe folgenden Code:

@posts = Post.joins(:user).joins(:blog).select

Dies soll alle Beiträge finden und sie sowie die zugehörigen Benutzer und Blogs zurückgeben. Jedoch Benutzer sind optional , was bedeutet , dass die , INNER JOINdie :joinsnicht zurückkehr viele Datensätze erzeugt.

Wie verwende ich dies, um LEFT OUTER JOINstattdessen eine zu generieren ?

Neil Middleton
quelle
Siehe auch LEFT OUTER JOIN in Rails 4
Yarin

Antworten:

111
@posts = Post.joins("LEFT OUTER JOIN users ON users.id = posts.user_id").
              joins(:blog).select
Neil Middleton
quelle
3
Was wäre, wenn Sie nur die Beiträge wollten, die keinen Benutzer hatten?
mcr
24
@mcr@posts = Post.joins("LEFT OUTER JOIN users ON users.id = posts.user_id").joins(:blog).where("users.id IS NULL").select
Linus Oleander
1
Benötigen Sie keinen Parameter? Sollte das nicht sein select('posts.*')?
Kevin Sylvestre
In Rails 3 ist dies die einzige Möglichkeit, Ihre Joins wirklich zu kontrollieren und genau zu wissen, was los ist.
Joshua Pinter
75

Sie können dies tun, includes wie im Rails-Handbuch dokumentiert :

Post.includes(:comments).where(comments: {visible: true})

Ergebnisse in:

SELECT "posts"."id" AS t0_r0, ...
       "comments"."updated_at" AS t1_r5
FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
WHERE (comments.visible = 1)
WuTangTan
quelle
14
Aus meinen Tests includesgeht kein Join hervor, sondern eine separate Abfrage, um die Zuordnung zu erhalten. So wird N + 1 vermieden, jedoch nicht auf die gleiche Weise wie bei einem JOIN, bei dem die Datensätze in einer Abfrage abgerufen werden.
Kris
7
@Kris Du hast in gewisser Weise recht. Dies ist etwas includes, auf das Sie achten
WuTangTan
4
Dies beantwortet die Frage nur teilweise, da includes2 Abfragen anstelle von a generiert werden, JOINwenn Sie keine benötigen WHERE.
Rodrigue
14
Dies generiert eine Warnung in Rails 4, sofern Sie nicht auch hinzufügen references(:comments). Darüber hinaus führt dies dazu, dass alle zurückgegebenen Kommentare aufgrund von includes, was möglicherweise nicht das ist, was Sie möchten , eifrig in den Speicher geladen werden .
Derek Prior
2
Um dies noch "Railsy" zu machen : Post.includes(:comments).where(comments: {visible: true}). Auf diese Weise müssen Sie auch nicht verwenden references.
Michael
11

Ich bin ein großer Fan des Squeel-Edelsteins :

Post.joins{user.outer}.joins{blog}

Es unterstützt sowohl innerund outerverbindet, sowie die Fähigkeit , eine Klasse / Typ für polymorphe belongs_to Beziehungen zu spezifizieren.

plainjimbo
quelle
10

Verwendung eager_load:

@posts = Post.eager_load(:user)
Ricardo
quelle
8

Wenn Sie ActiveRecord::Base#joinseine benannte Zuordnung übergeben, wird standardmäßig eine INNERE VERBINDUNG ausgeführt. Sie müssen eine Zeichenfolge übergeben, die Ihren LEFT OUTER JOIN darstellt.

Aus der Dokumentation :

:joins- Entweder ein SQL-Fragment für zusätzliche LEFT JOIN comments ON comments.post_id = idVerknüpfungen wie " " (selten erforderlich), benannte Zuordnungen in derselben Form, die für die :includeOption verwendet wird, die einen INNER JOIN für die zugehörigen Tabellen ausführt, oder ein Array, das eine Mischung aus beiden Zeichenfolgen enthält und benannte Assoziationen.

Wenn der Wert eine Zeichenfolge ist, werden die Datensätze schreibgeschützt zurückgegeben, da sie Attribute haben, die nicht den Spalten der Tabelle entsprechen. Übergeben, um :readonly => falsezu überschreiben.

DBA
quelle
7

In activerecord befindet sich eine left_outer_joins- Methode. Sie können es so verwenden:

@posts = Post.left_outer_joins(:user).joins(:blog).select
Ahmad Hussain
quelle
1
Dies scheint in Rails 3 nicht zu existieren, was das Poster verlangt.
Cesoid
Richtig; Dies wurde in Rails 5.0.0 eingeführt.
Ollie Bennett
4

Gute Nachrichten, Rails 5 unterstützt jetzt LEFT OUTER JOIN. Ihre Anfrage würde nun folgendermaßen aussehen:

@posts = Post.left_outer_joins(:user, :blog)
Dex
quelle
0
class User < ActiveRecord::Base
     has_many :friends, :foreign_key=>"u_from",:class_name=>"Friend"
end

class Friend < ActiveRecord::Base
     belongs_to :user
end


friends = user.friends.where(:u_req_status=>2).joins("LEFT OUTER JOIN users ON users.u_id = friends.u_to").select("friend_id,u_from,u_to,u_first_name,u_last_name,u_email,u_fbid,u_twtid,u_picture_url,u_quote")
Jigar Bhatt
quelle