2010-07-14 10 views
85

Tengo el siguiente código:IZQUIERDA EXTERIOR une en Rails 3

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

que está destinado a encontrar todos los puestos y ellos y los usuarios y blogs asociados regresar. Sin embargo, los usuarios son opcionales, lo que significa que el INNER JOIN que :joins genera no devuelve muchos registros.

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

+0

Véase también [Unión externa izquierda en los carriles 4] (http://stackoverflow.com/questions/24358805/left-outer-join-in-rails-4/35363012) – Yarin

Respuesta

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

¿Qué pasa si solo quieres las publicaciones que no tienen ningún usuario? – mcr

+24

@mcr '@posts = Post.joins (" usuarios IZQUIERDA OUTER JOIN ON users.id = posts.user_id "). Joins (: blog) .where (" users.id IS NULL "). Selecciona' – Oleander

+1

No seleccione necesita un param?¿No debería ser esto 'select (' posts. * ') '? –

8

De forma predeterminada, al pasar ActiveRecord::Base#joins una asociación con nombre, se realizará una UNIÓN INTERNA. Tendrás que pasar una cadena que represente tu UNIÓN EXTERIOR IZQUIERDA.

De the documentation:

:joins - O bien un fragmento de SQL para adicional se une como "LEFT JOIN comments ON comments.post_id = id" (poco frecuente), asociaciones nombradas en la misma forma utilizada para la opción :include, que llevará a cabo una INTERIOR JOIN en la (s) tabla (s) asociada (s), o una matriz que contiene una mezcla de ambas cadenas y asociaciones nombradas.

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

73

que puede hacer con esto con includesas documented in the Rails guide:

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

Esta es la forma correcta de "Rieles". – ricsrock

+13

De mis pruebas 'includes' no hace un join, sino una consulta separada para obtener la assosiation. Por lo tanto, evita N + 1, pero no de la misma manera que JOIN, donde los registros se obtienen en una consulta. – Kris

+6

@Kris Tienes razón, en cierto modo. Es algo que hay que tener en cuenta porque la función 'includes' hace ambas cosas, según el contexto en el que la estés usando. La guía de Rails lo explica mejor que yo si lees la totalidad de la sección 12: http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations – WuTangTan

11

Soy un gran fan de la squeel gem:

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

Admite ambas inner y outer uniones, así como la capacidad de especificar una clase/tipo para las relaciones polimórficas belongs_to.

+0

¿Para qué era el voto a favor? – plainjimbo

+1

Creo que fue un principio 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

Uso eager_load:

@posts = Post.eager_load(:user) 
6

Hay un método left_outer_joins en activerecord. Se puede utilizar de esta manera:

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

Esto no parece existir en Rails 3, que es lo que el cartel está pidiendo. – cesoid

+0

Correcto; esto fue introducido en Rails 5.0.0. –

4

Buenas noticias, carriles 5 ahora soporta LEFT OUTER JOIN. Su consulta sería ahora el siguiente aspecto:

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