L'ESTERNO SINISTRO si unisce a Rails 3


86

Ho il codice seguente:

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

che ha lo scopo di trovare tutti i post e restituirli e gli utenti e i blog associati. Tuttavia, gli utenti sono facoltativi, il che significa che INNER JOINciò che :joinsgenera non restituisce molti record.

Come lo uso per generare un LEFT OUTER JOINinvece?


Risposte:


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

3
e se volessi solo i post che non hanno utenti?
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
Non è necessario selezionare un parametro? Non dovrebbe essere questo select('posts.*')?
Kevin Sylvestre

In Rails 3, questo è l'unico modo per avere un vero controllo sui tuoi join e sapere esattamente cosa sta succedendo.
Joshua Pinter,

75

Puoi farlo con includes come documentato nella guida Rails :

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

Risultati 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)

14
Dai miei test includesnon viene effettuato un join, ma una query separata per ottenere l'associazione. Quindi evita N + 1, ma non allo stesso modo di un JOIN in cui i record vengono recuperati in una query.
Kris

7
@Kris Hai ragione, in un certo senso. È qualcosa a cui devi prestare attenzione perché la includesfunzione fa entrambe le cose, a seconda del contesto in cui la stai usando. La guida di Rails lo spiega meglio di quanto potrei se leggessi l'intera sezione 12: guides.rubyonrails.org/ ...
WuTangTan

4
Questo risponde solo in parte alla domanda perché includesgenererà 2 query invece di una JOINse non hai bisogno di WHERE.
Rodrigue

14
Questo genererà un avviso in Rails 4 a meno che tu non aggiunga anche references(:comments). Inoltre, questo farà sì che tutti i commenti restituiti vengano caricati in memoria a causa di includes, che probabilmente non è quello che desideri.
Derek Prior il

2
Per rendere questo ancora più "Railsy": Post.includes(:comments).where(comments: {visible: true}). In questo modo non è nemmeno necessario utilizzare references.
michael

11

Sono un grande fan della gemma squeel :

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

Supporta entrambi i join innere outer, oltre alla possibilità di specificare una classe / tipo per le relazioni appartiene_to polimorfico.


10

Usa eager_load:

@posts = Post.eager_load(:user)

8

Per impostazione predefinita, quando si passa ActiveRecord::Base#joinsun'associazione denominata, verrà eseguita un'INNER JOIN. Dovrai passare una stringa che rappresenta il tuo LEFT OUTER JOIN.

Dalla documentazione :

:joins- O un frammento SQL per join aggiuntivi come " LEFT JOIN comments ON comments.post_id = id" (raramente necessario), associazioni denominate nella stessa forma utilizzata per:include opzione, che eseguirà un INNER JOIN sulle tabelle associate o un array contenente una combinazione di entrambe le stringhe e associazioni denominate.

Se il valore è una stringa, i record verranno restituiti in sola lettura poiché avranno attributi che non corrispondono alle colonne della tabella. Passa :readonly => falseper ignorare.


7

C'è un metodo left_outer_joins in activerecord. Puoi usarlo in questo modo:

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

1
Questo non sembra esistere in Rails 3, che è ciò che chiede il poster.
cesoid

Corretta; questo è stato introdotto in Rails 5.0.0.
Ollie Bennett,

4

Buone notizie, Rails 5 ora supporta LEFT OUTER JOIN. La tua query ora sarebbe simile a:

@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")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.