LEFT OUTER łączy w Rails 3


86

Mam następujący kod:

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

co ma na celu znalezienie wszystkich postów i zwrócenie ich oraz powiązanych użytkowników i blogów. Jednak użytkownicy są opcjonalni, co oznacza, że ​​to, INNER JOINco :joinsgeneruje, nie zwraca wielu rekordów.

Jak użyć tego do wygenerowania LEFT OUTER JOINzamiast tego?


Odpowiedzi:


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

3
co by było, gdybyś chciał tylko postów, które nie miały użytkownika?
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
Czy opcja Select nie potrzebuje parametru? Nie powinno tak być select('posts.*')?
Kevin Sylvestre

W Railsach 3 jest to jedyny sposób, aby mieć prawdziwą kontrolę nad połączeniami i wiedzieć dokładnie, co się dzieje.
Joshua Pinter,

75

Możesz to zrobić includes zgodnie z opisem w przewodniku po Railsach :

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

Prowadzi do:

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
Z moich testów includesnie robi złączenia, ale osobne zapytanie, aby uzyskać skojarzenie. Więc unika N + 1, ale nie w taki sam sposób, jak JOIN, w którym rekordy są pobierane w jednym zapytaniu.
Kris

7
@Kris W pewnym sensie masz rację. Jest to coś, na co musisz uważać, ponieważ ta includesfunkcja robi jedno i drugie, w zależności od kontekstu, w którym jej używasz. Przewodnik po Railsach wyjaśnia to lepiej niż ja, gdybym przeczytał całą sekcję 12: Guides.rubyonrails.org/ …
WuTangTan

4
To tylko częściowo odpowiada na pytanie, ponieważ includeswygeneruje 2 zapytania zamiast jednego, JOINjeśli nie potrzebujesz WHERE.
Rodrigue,

14
Spowoduje to wygenerowanie ostrzeżenia w Rails 4, chyba że również dodasz references(:comments). Dodatkowo spowoduje to, że wszystkie zwrócone komentarze będą chętnie ładowane do pamięci z powodu includes, co prawdopodobnie nie jest tym, czego chcesz.
Derek Prior

2
Aby ten jeszcze bardziej „Railsy”: Post.includes(:comments).where(comments: {visible: true}). W ten sposób nie musisz też używać references.
michael

11

Jestem wielkim fanem piskląt :

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

Obsługuje zarówno połączenia, jak inneri outerłączenia, a także możliwość określenia klasy / typu dla polimorficznych relacji przynależności_do.



8

Domyślnie po przekazaniu ActiveRecord::Base#joinsnazwanego powiązania wykona INNER JOIN. Będziesz musiał przekazać ciąg reprezentujący LEWE ZEWNĘTRZNE POŁĄCZENIE.

Z dokumentacji :

:joins- Albo fragment SQL dla dodatkowych łączeń, takich jak „ LEFT JOIN comments ON comments.post_id = id” (rzadko potrzebne), nazwane asocjacje w tej samej formie :include, co w przypadku opcji, które wykonają INNER JOIN na skojarzonej tabeli (tabelach) lub tablica zawierająca mieszaninę obu łańcuchów i nazwane stowarzyszenia.

Jeśli wartość jest ciągiem, rekordy zostaną zwrócone tylko do odczytu, ponieważ będą miały atrybuty, które nie odpowiadają kolumnom tabeli. Przejdź :readonly => falsedo zastąpienia.



4

Dobra wiadomość, Rails 5 obsługuje teraz LEFT OUTER JOIN. Twoje zapytanie wyglądałoby teraz następująco:

@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")
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.