LEFT OUTER tham gia vào Rails 3


86

Tôi có mã sau:

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

có nghĩa là tìm tất cả các bài đăng và trả lại chúng cũng như những người dùng và blog được liên kết. Tuy nhiên, người sử dụng là không bắt buộc có nghĩa là INNER JOINđó :joinstạo ra không trở về rất nhiều hồ sơ.

Làm cách nào để sử dụng điều này để tạo một LEFT OUTER JOINthay thế


Câu trả lời:


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

3
điều gì xảy ra nếu bạn chỉ muốn các Bài đăng không có người dùng?
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
Lựa chọn không cần một tham số? Điều này không nên select('posts.*')?
Kevin Sylvestre

Trong Rails 3, đây là cách duy nhất để có quyền kiểm soát thực sự đối với các phép nối của bạn và biết chính xác điều gì đang xảy ra.
Joshua Pinter

75

Bạn có thể làm điều này với includes như được ghi trong hướng dẫn Rails :

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

Kết quả trong:

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
Từ các thử nghiệm của tôi includeskhông thực hiện một phép nối, mà là một truy vấn riêng biệt để lấy phân tích. Vì vậy, nó tránh N + 1, nhưng không giống như một JOIN trong đó các bản ghi được tìm nạp trong một truy vấn.
Kris

7
@Kris Bạn nói đúng, theo một cách nào đó. Đó là một cái gì đó bạn cần phải coi chừng vì includeschức năng hiện cả hai, tùy thuộc vào bối cảnh mà bạn đang sử dụng nó trong Hướng dẫn Rails giải thích nó tốt hơn tôi có thể nếu bạn đọc toàn bộ phần 12:. Guides.rubyonrails.org/ …
WuTangTan 20/02/13

4
Điều này chỉ trả lời một phần câu hỏi vì includessẽ tạo ra 2 truy vấn thay vì a JOINnếu bạn không có nhu cầu WHERE.
Rodrigue

14
Điều này sẽ tạo ra một cảnh báo trong Rails 4 trừ khi bạn thêm vào references(:comments). Ngoài ra, điều này sẽ làm cho tất cả các nhận xét trả về được tải trong bộ nhớ do includescó thể không phải là những gì bạn muốn.
Derek Prior

2
Để làm điều này thậm chí còn nhiều hơn "Railsy": Post.includes(:comments).where(comments: {visible: true}). Cách này bạn cũng không cần sử dụng references.
michael

11

Tôi là một fan hâm mộ lớn của đá quý tiếng kêu :

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

Nó hỗ trợ cả hai inneroutertham gia, cũng như khả năng chỉ định một lớp / kiểu cho các mối quan hệ thuộc_to đa hình.


10

Sử dụng eager_load:

@posts = Post.eager_load(:user)

8

Theo mặc định, khi bạn vượt qua ActiveRecord::Base#joinsmột liên kết được đặt tên, nó sẽ thực hiện THAM GIA GÓC. Bạn sẽ phải chuyển một chuỗi đại diện cho THAM GIA LEFT OUTER của bạn.

Từ tài liệu :

:joins- Một đoạn SQL cho các phép nối bổ sung như " LEFT JOIN comments ON comments.post_id = id" (hiếm khi cần), các liên kết được đặt tên ở cùng một dạng được sử dụng cho :includetùy chọn, sẽ thực hiện một INNER JOIN trên (các) bảng được liên kết hoặc một mảng chứa hỗn hợp của cả hai chuỗi và các hiệp hội được đặt tên.

Nếu giá trị là một chuỗi, thì các bản ghi sẽ được trả về ở chế độ chỉ đọc vì chúng sẽ có các thuộc tính không tương ứng với các cột của bảng. Vượt qua :readonly => falseđể ghi đè.


7

Có một phương thức left_outer_joins trong activerecord. Bạn có thể sử dụng nó như thế này:

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

1
Điều này dường như không tồn tại trong Rails 3, đó là những gì mà người đăng đang yêu cầu.
cesoid

Chính xác; điều này đã được giới thiệu trong Rails 5.0.0.
Ollie Bennett

4

Tin tốt là Rails 5 hiện đã hỗ trợ LEFT OUTER JOIN. Truy vấn của bạn bây giờ sẽ giống như sau:

@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")
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.