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 це єдиний спосіб мати справжній контроль над своїми приєднаннями і точно знати, що відбувається.
Джошуа Пінтер,

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, але не так само, як JOIN, де записи отримуються в одному запиті.
Kris

7
@Kris Ти певним чином маєш рацію. Це те, на що потрібно стежити, оскільки includesфункція виконує обидва дії, залежно від контексту, в якому ви її використовуєте. Посібник Rails пояснює це краще, ніж я міг, якби ви прочитали весь розділ 12: guides.rubyonrails.org/ ...
WuTangTan

4
Це лише частково відповідає на питання, оскільки includesгенерує 2 запити замість, JOINякщо у вас немає потреби в WHERE.
Родріге

14
Це створить попередження в Rails 4, якщо ви також не додасте references(:comments). Крім того, це призведе до того, що всі повернуті коментарі будуть охоче завантажені в пам'ять через includes, що, можливо, не те, що ви хочете.
Дерек Пріор

2
Щоб зробити це ще більш «Railsy»: Post.includes(:comments).where(comments: {visible: true}). Таким чином вам також не потрібно користуватися references.
Майкл

11

Я великий шанувальник самоцвіту :

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

Він підтримує як об'єднання, так innerі outerоб'єднання, а також можливість вказувати клас / тип для поліморфних відносин принадлежності_.



8

За замовчуванням, коли ви передаєте ActiveRecord::Base#joinsіменовану асоціацію, вона виконуватиме ВНУТРІШНЄ ПРИЄДНАННЯ. Вам доведеться передати рядок, що представляє ваше ЛІВО ВНЕШНЄ ПРИЄДНАННЯ.

З документації :

:joins- Або фрагмент SQL для додаткових об'єднань, таких як " LEFT JOIN comments ON comments.post_id = id" (рідко потрібно), іменовані асоціації в тій самій формі, що і для:include параметра, який виконуватиме ВНУТРІШНЄ СПІЛКУВАННЯ у пов'язаних таблицях, або масив, що містить суміш обох рядків і названі асоціації.

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



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")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.