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?


Jawaban:


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

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)

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.


10

Penggunaan eager_load:

@posts = Post.eager_load(:user)

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.



4

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

@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")
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.