LEFT OUTER se une a Rails 3

86

Tengo el siguiente código:

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

que está destinado a encontrar todas las publicaciones y devolverlas y los usuarios y blogs asociados. Sin embargo, los usuarios son opcionales, lo que significa que el INNER JOINque :joinsgenera no devuelve muchos registros.

¿Cómo utilizo esto para generar un LEFT OUTER JOINen su lugar?

Neil Middleton
fuente
Consulte también LEFT OUTER JOIN en Rails 4
Yarin

Respuestas:

111
@posts = Post.joins("LEFT OUTER JOIN users ON users.id = posts.user_id").
              joins(:blog).select
Neil Middleton
fuente
3
¿Y si solo quisieras las publicaciones que no tienen usuario?
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
¿No necesita seleccionar un parámetro? ¿No debería ser esto select('posts.*')?
Kevin Sylvestre
En Rails 3, esta es la única forma de tener un verdadero control sobre sus combinaciones y saber exactamente qué está pasando.
Joshua Pinter
75

Puede hacer con esto includes como se documenta en la guía Rails :

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

Resultados 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)
WuTangTan
fuente
14
De mis pruebas includesno hace una combinación, sino una consulta separada para obtener la asociación. Por lo tanto, evita N + 1, pero no de la misma manera que JOIN, donde los registros se obtienen en una consulta.
Kris
7
@Kris Tienes razón, en cierto modo. Es algo que debe tener en cuenta porque la includesfunción hace ambas cosas, dependiendo del contexto en el que la esté utilizando. La guía Rails lo explica mejor que yo si leyera la sección 12 completa: guides.rubyonrails.org/ …
WuTangTan
4
Esto solo responde parcialmente a la pregunta porque includesgenerará 2 consultas en lugar de una JOINsi no necesita el WHERE.
Rodrigue
14
Esto generará una advertencia en Rails 4 a menos que también agregue references(:comments). Además, esto hará que todos los comentarios devueltos se carguen ansiosamente en la memoria debido a includesque posiblemente no sea lo que desea.
Derek Prior
2
Para hacer esto aún más "Railsy": Post.includes(:comments).where(comments: {visible: true}). De esta manera tampoco es necesario usar references.
Michael
11

Soy un gran admirador de la joya squeel :

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

Es compatible con ambos innery se outerune, así como la capacidad de especificar una clase / tipo para las relaciones polimórficas pertenecen a.

plainjimbo
fuente
10

Utilizar eager_load:

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

De forma predeterminada, cuando pasa ActiveRecord::Base#joinsuna asociación con nombre, realizará una INNER JOIN. Tendrá que pasar una cadena que represente su LEFT OUTER JOIN.

De la documentación :

:joins- O un fragmento SQL para combinaciones adicionales como " LEFT JOIN comments ON comments.post_id = id" (rara vez se necesita), asociaciones con nombre en la misma forma que se usa para la :includeopción, que realizará una INNER JOIN en las tablas asociadas, o una matriz que contenga una mezcla de ambas cadenas y asociaciones nombradas.

Si el valor es una cadena, los registros se devolverán como de solo lectura, ya que tendrán atributos que no se corresponden con las columnas de la tabla. Pase :readonly => falsepara anular.

DBA
fuente
7

Hay una left_outer_joins método en el activerecord. Puedes usarlo así:

@posts = Post.left_outer_joins(:user).joins(:blog).select
Ahmad Hussain
fuente
1
Esto no parece existir en Rails 3, que es lo que pide el póster.
cesoide
Correcto; esto se introdujo en Rails 5.0.0.
Ollie Bennett
4

Buenas noticias, Rails 5 ahora es compatible LEFT OUTER JOIN. Su consulta ahora se vería así:

@posts = Post.left_outer_joins(:user, :blog)
Dex
fuente
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
fuente