LEFT OUTER เข้าร่วมใน Rails 3


86

ฉันมีรหัสต่อไปนี้:

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

ซึ่งมีจุดมุ่งหมายเพื่อค้นหาโพสต์ทั้งหมดและส่งคืนและผู้ใช้และบล็อกที่เกี่ยวข้อง อย่างไรก็ตามผู้ใช้เป็นทางเลือกซึ่งหมายความว่าสิ่งINNER JOINที่:joinsสร้างขึ้นจะไม่ส่งคืนระเบียนจำนวนมาก

ฉันจะใช้สิ่งนี้เพื่อสร้างLEFT OUTER JOINแทนได้อย่างไร


คำตอบ:


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

3
จะเป็นอย่างไรหากคุณต้องการเฉพาะโพสต์ที่ไม่มีผู้ใช้
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
เลือกไม่จำเป็นต้องมีพารามิเตอร์? สิ่งนี้ไม่ควรselect('posts.*')หรือ
Kevin Sylvestre

ใน Rails 3 นี่เป็นวิธีเดียวที่จะควบคุมการรวมของคุณได้อย่างแท้จริงและรู้ว่าเกิดอะไรขึ้น
Joshua Pinter

75

คุณสามารถทำได้includes ตามที่ระบุไว้ในคู่มือ Rails :

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

ผลลัพธ์ใน:

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
จากการทดสอบของฉันincludesไม่ได้ทำการรวม แต่เป็นการสืบค้นแยกต่างหากเพื่อรับการผสม ดังนั้นจึงหลีกเลี่ยง N + 1 แต่ไม่ใช่ในลักษณะเดียวกับการเข้าร่วมที่มีการดึงข้อมูลในแบบสอบถามเดียว
Kris

7
@ คริสคุณพูดถูกในทาง เป็นสิ่งที่คุณต้องระวังเพราะincludesฟังก์ชั่นทำทั้งสองอย่างขึ้นอยู่กับบริบทที่คุณใช้คู่มือ Rails อธิบายได้ดีกว่าที่ฉันทำได้ถ้าคุณอ่านส่วนที่ 12 ทั้งหมด: guide.rubyonrails.org/ …
WuTangTan

4
นี่เป็นเพียงบางส่วนเท่านั้นที่ตอบคำถามเพราะincludesจะสร้าง 2 แบบสอบถามแทนที่จะเป็นJOINถ้าคุณไม่มีความต้องการWHERE.
Rodrigue

14
สิ่งนี้จะสร้างคำเตือนใน Rails 4 เว้นแต่คุณจะเพิ่มreferences(:comments)ด้วย นอกจากนี้สิ่งนี้จะทำให้ความคิดเห็นที่ส่งคืนทั้งหมดถูกโหลดอย่างกระตือรือร้นในหน่วยความจำเนื่องจากincludesสิ่งนี้อาจไม่ใช่สิ่งที่คุณต้องการ
Derek ก่อน

2
จะทำให้เรื่องนี้มากยิ่งขึ้น Post.includes(:comments).where(comments: {visible: true})"Railsy": referencesวิธีนี้คุณยังไม่จำเป็นต้องใช้
michael



8

โดยค่าเริ่มต้นเมื่อคุณผ่านActiveRecord::Base#joinsการเชื่อมโยงที่มีชื่อการเชื่อมโยงจะดำเนินการ INNER JOIN คุณจะต้องส่งสตริงที่แสดงถึง LEFT OUTER JOIN

จากเอกสารประกอบ :

:joins- อย่างใดอย่างหนึ่งส่วน SQL สำหรับการรวมเพิ่มเติมเช่น " LEFT JOIN comments ON comments.post_id = id" (ไม่ค่อยจำเป็น) ตั้งชื่อการเชื่อมโยงในรูปแบบเดียวกับที่ใช้สำหรับ:includeตัวเลือกซึ่งจะดำเนินการ INNER JOIN บนตารางที่เกี่ยวข้องหรืออาร์เรย์ที่มีส่วนผสมของทั้งสองสตริง และตั้งชื่อสมาคม

หากค่าเป็นสตริงระเบียนจะถูกส่งกลับแบบอ่านอย่างเดียวเนื่องจากจะมีแอตทริบิวต์ที่ไม่ตรงกับคอลัมน์ของตาราง ส่งผ่าน :readonly => falseเพื่อลบล้าง


7

มีเมธอดleft_outer_joinsใน activerecord คุณสามารถใช้งานได้ดังนี้:

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

1
ดูเหมือนจะไม่มีใน Rails 3 ซึ่งเป็นสิ่งที่ผู้โพสต์ขอ
cesoid

แก้ไข; สิ่งนี้ถูกนำมาใช้ใน Rails 5.0.0
Ollie Bennett

4

ข่าวดี Rails 5 รองรับLEFT OUTER JOINแล้ว คำถามของคุณจะมีลักษณะดังนี้:

@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")
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.