2010-07-14 47 views
85

Tôi có đoạn mã sau:LEFT OUTER tham gia trong Rails 3

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

đó có nghĩa là để tìm tất cả các bài viết và trả lại và người sử dụng có liên quan và các blog. Tuy nhiên, người dùng là tùy chọn có nghĩa là INNER JOIN rằng việc tạo ra :joins không trả lại nhiều bản ghi.

Làm cách nào để sử dụng tính năng này để tạo một LEFT OUTER JOIN thay thế?

+0

Xem thêm [LEFT OUTER JOIN in Rails 4] (http://stackoverflow.com/questions/24358805/left-outer-join-in-rails-4/35363012) – Yarin

Trả lời

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

nếu bạn chỉ muốn các Bài đăng không có người dùng thì sao? – mcr

+24

@mcr '@posts = Post.joins (" LEFT OUTER JOIN người dùng TRÊN users.id = posts.user_id "). Join (: blog) .where (" users.id IS NULL "). Select' – Oleander

+1

Doesn't chọn cần một param?Điều này có nên là 'select ('posts. *')'? –

8

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

Từ the documentation:

:joins - Hoặc một đoạn SQL để biết thêm tham gia như "LEFT JOIN comments ON comments.post_id = id", tên hiệp hội (hiếm khi cần thiết) theo hình thức tương tự được sử dụng để lựa chọn :include, mà sẽ thực hiện một INNER JOIN trên (các) bảng liên quan, hoặc một mảng có chứa một hỗn hợp của cả hai chuỗi và các liên kết được đặt tên.

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

73

Bạn có thể làm với điều này với includesas documented in the Rails guide:

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) 
+4

Đây là cách "Rails" chính xác. – ricsrock

+13

Từ các bài kiểm tra của tôi 'bao gồm' không làm một tham gia, nhưng một truy vấn riêng biệt để có được các assosiation. Vì vậy, nó tránh N + 1, nhưng không phải trong cùng một cách như một JOIN nơi các hồ sơ được lấy trong một truy vấn. – Kris

+6

@Kris Bạn nói đúng, theo cách nào đó. Đó là điều bạn cần phải chú ý vì chức năng 'include' thực hiện cả hai, tùy thuộc vào ngữ cảnh mà bạn đang sử dụng nó. 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: http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations – WuTangTan

11

Tôi là một fan hâm mộ lớn của squeel gem:

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

Hỗ trợ cả hai tham gia innerouter cũng như khả năng chỉ định lớp/loại cho mối quan hệ thuộc về đa hình.

+0

Điều gì đã làm cho downvote? – plainjimbo

+1

Tôi nghĩ rằng đó là một downvoter nguyên tắc =) –

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") 
10

Sử dụng eager_load:

@posts = Post.eager_load(:user) 
6

Có một phương pháp 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ì các poster được yêu cầu. – cesoid

+0

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

4

Tin tốt, Rails 5 hiện hỗ trợ LEFT OUTER JOIN. Truy vấn của bạn giờ sẽ trông giống như:

@posts = Post.left_outer_joins(:user, :blog) 
Các vấn đề liên quan