2010-07-14 2 views
85

У меня есть следующий код:LEFT OUTER включается в Rails 3

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

, который предназначен, чтобы найти все сообщения и возвращать их и связанные с ним пользователей и блоги. Однако пользователи являются необязательными, что означает, что INNER JOIN, который генерирует :joins, не возвращает много записей.

Как это использовать, чтобы сгенерировать LEFT OUTER JOIN?

+0

Смотрите также [LEFT OUTER JOIN в Rails 4] (http://stackoverflow.com/questions/24358805/left-outer-join-in-rails-4/35363012) – Yarin

ответ

110
@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' – Oleander

+1

Не делает выберите необходимый параметр?Разве это не должно быть 'select ('posts. *')'? –

8

По умолчанию, когда вы передаете ActiveRecord::Base#joins названную ассоциацию, она будет выполнять INNER JOIN. Вам нужно будет передать строку, представляющую ваш LEFT OUTER JOIN.

От the documentation:

:joins - либо фрагмент SQL для дополнительного присоединяется как «LEFT JOIN comments ON comments.post_id = id» (редко нужно), названных ассоциации в той же форме, используемой для опции :include, который будет выполнять ВНУТРЕННИЕ JOIN в связанной таблице (таблицах) или массиве, содержащем смесь обеих строк и названных ассоциаций.

Если значение представляет собой строку , то записи будут возвращаться только для чтения, так как они будут иметь атрибуты, которые не соответствуют столбцам таблицы. Передать :readonly => false переопределить.

73

Вы можете сделать с этим с includesas documented in the Rails guide:

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

Это правильный способ «Rails». – ricsrock

+13

Из моих тестов 'includes' не делает соединение, а отдельный запрос, чтобы получить ассоциацию. Таким образом, он избегает N + 1, но не так же, как JOIN, где записи извлекаются в одном запросе. – Kris

+6

@Kris Вы правы, в некотором роде. Это то, что вам нужно следить, потому что функция 'includes' выполняет оба варианта, в зависимости от контекста, в котором вы его используете. Справочник Rails объясняет это лучше, чем я мог, если вы прочитаете полный текст раздела 12: http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations – WuTangTan

11

Я большой поклонник squeel gem:

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

Он поддерживает как соединения inner, так и outer, а также возможность указывать класс/тип для полиморфных отношений belongs_to.

+0

Для чего был downvote? – plainjimbo

+1

Я думаю, что это был принцип downvoter =) –

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

Использование eager_load:

@posts = Post.eager_load(:user) 
6

Существует left_outer_joins метод ActiveRecord. Вы можете использовать его как это:

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

Это, похоже, не существует в Rails 3, о чем просит плакат. – cesoid

+0

Исправить; это было введено в Rails 5.0.0. –

4

Хорошие новости, Rails 5 теперь поддерживает LEFT OUTER JOIN. Ваш запрос будет выглядеть так:

@posts = Post.left_outer_joins(:user, :blog)