2010-07-14 4 views
85

Je le code suivant:LEFT OUTER rejoint dans Rails 3

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

qui vise à trouver tous les messages et les retourner et les utilisateurs associés et des blogs. Toutefois, les utilisateurs sont facultatifs, ce qui signifie que le INNER JOIN généré par :joins ne renvoie pas beaucoup d'enregistrements.

Comment l'utiliser pour générer un LEFT OUTER JOIN à la place?

+0

Voir aussi [LEFT OUTER JOIN dans Rails 4] (http://stackoverflow.com/questions/24358805/left-outer-join-in-rails-4/35363012) – Yarin

Répondre

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

Et si vous vouliez seulement les postes qui n'avaient pas d'utilisateur? – mcr

+24

@mcr '@posts = Post.joins (" LEFT OUTER JOIN les utilisateurs sur users.id = posts.user_id "). Joint (: blog) .where (" users.id IS NULL "). Select' – Oleander

+1

sélectionnez besoin d'un param?Cela ne devrait-il pas être 'select ('posts. *')'? –

8

Par défaut, lorsque vous transmettez une association nommée ActiveRecord::Base#joins, il exécute une INNER JOIN. Vous devrez passer une chaîne représentant votre LEFT OUTER JOIN.

De the documentation:

:joins - Soit un fragment SQL pour supplémentaire se joint comme "LEFT JOIN comments ON comments.post_id = id" (rarement nécessaire), les associations nommées dans la même forme utilisée pour l'option :include, qui effectuera une INNER JOIN sur la ou les tables associées, ou un tableau contenant un mélange des deux chaînes et des associations nommées.

Si la valeur est une chaîne , les enregistrements seront renvoyés en lecture seule puisqu'ils auront des attributs qui ne correspondent pas aux colonnes de la table. Passez :readonly => false pour remplacer.

73

Vous pouvez faire avec cela avec includesas documented in the Rails guide:

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

Résultats dans:

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) 
+4

C'est la manière correcte de "Rails". – ricsrock

+13

De mes tests 'includes' ne fait pas de jointure, mais une requête séparée pour obtenir l'assosiation. Donc, il évite N + 1, mais pas de la même manière qu'un JOIN où les enregistrements sont récupérés dans une requête. – Kris

+6

@Kris Vous avez raison, d'une certaine manière. C'est quelque chose que vous devez surveiller car la fonction 'includes' fait les deux, selon le contexte dans lequel vous l'utilisez. Le guide Rails l'explique mieux que je ne le pourrais si vous lisiez l'intégralité de la section 12: http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations – WuTangTan

11

Je suis un grand fan de la squeel gem:

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

Il prend en charge les jointures inner et outer, ainsi que la possibilité de spécifier une classe/type pour les relations polymorphes belongs_to.

+0

A quoi servait le downvote? – plainjimbo

+1

Je pense que c'était un principe downvoter =) –

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") 
10

Utilisation eager_load:

@posts = Post.eager_load(:user) 
6

Il existe une méthode left_outer_joins dans 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, qui est ce que l'affiche demande. – cesoid

+0

Correct; Cela a été introduit dans Rails 5.0.0. –

4

bonnes nouvelles, Rails 5 prend désormais en charge LEFT OUTER JOIN. Votre requête serait maintenant ressembler à:

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