LEFT OUTER rejoint les rails 3


86

J'ai le code suivant:

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

qui est destiné à trouver tous les articles et à les renvoyer ainsi qu'aux utilisateurs et blogs associés. Cependant, les utilisateurs sont facultatifs, ce qui signifie que le INNER JOINqui :joinsgénère ne renvoie pas beaucoup d'enregistrements.

Comment utiliser ceci pour générer un à la LEFT OUTER JOINplace?


Réponses:


111
@posts = Post.joins("LEFT OUTER JOIN users ON users.id = posts.user_id").
              joins(:blog).select

3
Et si vous ne vouliez que les messages qui n'avaient pas d'utilisateur?
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
La sélection n'a-t-elle pas besoin d'un paramètre? Cela ne devrait-il pas être select('posts.*')?
Kevin Sylvestre

Dans Rails 3, c'est le seul moyen d'avoir un véritable contrôle sur vos jointures et de savoir exactement ce qui se passe.
Joshua Pinter

75

Vous pouvez le faire avec includes comme indiqué dans le guide Rails :

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

Résulte en:

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)

14
De mes tests includesne fait pas une jointure, mais une requête distincte pour obtenir l'assosiation. Cela évite donc N + 1, mais pas de la même manière qu'un JOIN où les enregistrements sont récupérés en une seule requête.
Kris

7
@Kris Vous avez raison, d'une certaine manière. C'est quelque chose dont vous devez faire attention car la includesfonction fait les deux, selon le contexte dans lequel vous l'utilisez. Le guide Rails l'explique mieux que moi si vous lisiez l'intégralité de la section 12: guides.rubyonrails.org/ …
WuTangTan

4
Cela ne répond que partiellement à la question car includescela générera 2 requêtes au lieu d'une JOINsi vous n'avez pas besoin de WHERE.
Rodrigue

14
Cela générera un avertissement dans Rails 4 sauf si vous ajoutez également references(:comments). En outre, cela entraînera le chargement hâtif de tous les commentaires renvoyés en mémoire en raison de includes, ce qui n'est peut-être pas ce que vous souhaitez.
Derek Prior

2
Pour rendre cela encore plus « Railsy »: Post.includes(:comments).where(comments: {visible: true}). De cette façon, vous n'avez pas non plus besoin d'utiliser references.
michael

11

Je suis un grand fan de la gemme squeel :

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

Il prend en charge à la fois inneret les outerjointures, ainsi que la possibilité de spécifier une classe / un type pour les relations polymorphes appart_to.


10

Utilisez eager_load:

@posts = Post.eager_load(:user)

8

Par défaut, lorsque vous passez ActiveRecord::Base#joinsune association nommée, elle effectuera une INNER JOIN. Vous devrez passer une chaîne représentant votre JOINTURE EXTÉRIEURE GAUCHE.

De la documentation :

:joins- Soit un fragment SQL pour des jointures supplémentaires comme " LEFT JOIN comments ON comments.post_id = id" (rarement nécessaire), des associations nommées sous la même forme que celle utilisée pour l' :includeoption, qui effectuera un INNER JOIN sur la ou les tables associées, soit un tableau contenant un mélange des deux chaînes et les associations nommées.

Si la valeur est une chaîne, les enregistrements seront renvoyés en lecture seule car ils auront des attributs qui ne correspondent pas aux colonnes de la table. Passer :readonly => falsepour passer outre.


7

Il y a une left_outer_joins méthode activerecord. Vous pouvez l'utiliser comme ceci:

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

1
Cela ne semble pas exister dans Rails 3, c'est ce que l'affiche demande.
cesoid

Correct; cela a été introduit dans Rails 5.0.0.
Ollie Bennett

4

Bonne nouvelle, Rails 5 prend désormais en charge LEFT OUTER JOIN. Votre requête ressemblerait maintenant à:

@posts = Post.left_outer_joins(:user, :blog)

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")
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.