LEFT OUTER bergabung di Rails 3

86

Saya memiliki kode berikut:

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

yang dimaksudkan untuk menemukan semua kiriman dan mengembalikannya serta pengguna dan blog terkait. Namun, pengguna bersifat opsional yang berarti bahwa INNER JOINyang :joinsmenghasilkan tidak mengembalikan banyak catatan.

Bagaimana cara menggunakan ini untuk menghasilkan sebagai LEFT OUTER JOINgantinya?

Neil Middleton
sumber
Lihat juga LEFT OUTER JOIN di Rails 4
Yarin

Jawaban:

111
@posts = Post.joins("LEFT OUTER JOIN users ON users.id = posts.user_id").
              joins(:blog).select
Neil Middleton
sumber
3
bagaimana jika Anda hanya menginginkan Posting yang tidak memiliki pengguna?
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
Bukankah pilih butuh parameter? Bukankah seharusnya demikian select('posts.*')?
Kevin Sylvestre
Di Rails 3, ini adalah satu-satunya cara untuk memiliki kendali sejati atas gabungan Anda dan tahu persis apa yang terjadi.
Joshua Pinter
75

Anda dapat melakukan ini dengan includes seperti yang didokumentasikan di panduan Rails :

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

Hasil dalam:

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
sumber
14
Dari pengujian saya includestidak melakukan join, tetapi query terpisah untuk mendapatkan assosiation. Jadi ini menghindari N + 1, tetapi tidak dengan cara yang sama seperti JOIN di mana rekaman diambil dalam satu kueri.
Kris
7
@Kamu benar, dalam satu hal. Itu adalah sesuatu yang perlu Anda perhatikan karena includesfungsinya melakukan keduanya, tergantung pada konteks tempat Anda menggunakannya. Panduan Rails menjelaskannya lebih baik daripada yang saya bisa jika Anda membaca keseluruhan bagian 12: Guideline.rubyonrails.org/ …
WuTangTan
4
Ini hanya menjawab sebagian pertanyaan karena includesakan menghasilkan 2 query, bukan sebuah JOINjika Anda tidak membutuhkan WHERE.
Rodrigue
14
Ini akan menghasilkan peringatan di Rails 4 kecuali Anda juga menambahkan references(:comments). Selain itu, ini akan menyebabkan semua komentar yang dikembalikan ingin dimuat dalam memori karena includes, yang mungkin bukan yang Anda inginkan.
Derek Sebelum
2
Untuk membuat ini bahkan lebih "Railsy": Post.includes(:comments).where(comments: {visible: true}). Dengan cara ini Anda juga tidak perlu menggunakan references.
michael
11

Saya penggemar berat squeel gem :

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

Ini mendukung keduanya innerdan outerbergabung, serta kemampuan untuk menentukan kelas / tipe untuk hubungan milik_to polimorfik.

plainjimbo.dll
sumber
10

Penggunaan eager_load:

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

Secara default ketika Anda melewati ActiveRecord::Base#joinsasosiasi bernama, itu akan melakukan INNER JOIN. Anda harus melewatkan string yang mewakili LEFT OUTER JOIN Anda.

Dari dokumentasi :

:joins- Fragmen SQL untuk gabungan tambahan seperti " LEFT JOIN comments ON comments.post_id = id" (jarang diperlukan), asosiasi bernama dalam bentuk yang sama yang digunakan untuk :includeopsi, yang akan melakukan INNER JOIN pada tabel terkait, atau larik yang berisi campuran kedua string dan asosiasi bernama.

Jika nilainya adalah string, record akan dikembalikan hanya-baca karena memiliki atribut yang tidak sesuai dengan kolom tabel. Lewati :readonly => falseuntuk mengganti.

DBA
sumber
7

Ada metode left_outer_joins di activerecord . Anda bisa menggunakannya seperti ini:

@posts = Post.left_outer_joins(:user).joins(:blog).select
Ahmad Hussain
sumber
1
Ini sepertinya tidak ada di Rails 3, yang diminta oleh poster.
cesoid
Benar; ini diperkenalkan di Rails 5.0.0.
Ollie Bennett
4

Kabar baiknya, Rails 5 sekarang mendukung LEFT OUTER JOIN. Kueri Anda sekarang akan terlihat seperti ini:

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