LEFT OUTER присоединяется к Rails 3

86

У меня такой код:

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

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

Как мне использовать это для создания LEFT OUTER JOINвместо этого?

Нил Миддлтон
источник
Смотрите также LEFT OUTER JOIN in Rails 4
Yarin

Ответы:

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
Линус Олеандр
1
Не выбирает нужен параметр? Разве этого не должно быть select('posts.*')?
Кевин Сильвестр,
В Rails 3 это единственный способ полностью контролировать свои объединения и точно знать, что происходит.
Джошуа Пинтер
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)
WuTangTan
источник
14
Из моих тестов includesне выполняется соединение, а выполняется отдельный запрос для получения ассоциации. Таким образом, он избегает N + 1, но не так, как JOIN, когда записи выбираются в одном запросе.
Крис
7
@Kris.В каком-то смысле ты прав. Это то, на что вам нужно обратить внимание, потому что includesфункция делает и то, и другое, в зависимости от контекста, в котором вы ее используете. Руководство по Rails объясняет это лучше, чем я мог бы, если бы вы полностью прочитали раздел 12: guides.rubyonrails.org/ …
WuTangTan
4
Это лишь частично отвечает на вопрос, потому что includesбудет генерировать 2 запроса вместо одного, JOINесли вам не нужен WHERE.
Rodrigue
14
Это сгенерирует предупреждение в Rails 4, если вы также не добавите references(:comments). Кроме того, это приведет к тому, что все возвращенные комментарии будут загружены в память из-за того includes, что, возможно, не то, что вам нужно.
Derek Prior
2
Чтобы сделать это еще более «Railsy»: Post.includes(:comments).where(comments: {visible: true}). Таким образом, вам также не нужно использовать references.
Майкл
11

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

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

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

простой джимбо
источник
10

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

@posts = Post.eager_load(:user)
Рикардо
источник
8

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

Из документации :

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

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

DBA
источник
7

В activerecord есть метод left_outer_joins. Вы можете использовать это так:

@posts = Post.left_outer_joins(:user).joins(:blog).select
Ахмад Хусейн
источник
1
Кажется, этого не существует в Rails 3, о чем и просит плакат.
cesoid
Верный; это было введено в Rails 5.0.0.
Олли Беннетт
4

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

@posts = Post.left_outer_joins(:user, :blog)
Dex
источник
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")
Джигар Бхатт
источник