Załóżmy, że musisz uzyskać nazwę użytkownika z pierwszych pięciu postów. Szybko piszesz poniższe zapytanie i cieszysz się weekendem.
posts = Post.limit(5)
posts.each do |post|
puts post.user.name
end
Dobrze. Spójrzmy jednak na zapytania
Post Load (0.5ms) SELECT `posts`.* FROM `posts` LIMIT 5
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
1 query aby pobrać wszystkie posts i 1 query aby pobrać users dla każdego posta daje łącznie 6 queries . Sprawdź poniższe rozwiązanie, które robi to samo, tylko w 2 queries :
posts = Post.includes(:user).limit(5)
posts.each do |post|
puts post.user.name
end
#####
Post Load (0.3ms) SELECT `posts`.* FROM `posts` LIMIT 5
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 2)
Jest jedna mała różnica. Dodaj includes(:posts) na Twoje zapytanie i rozwiązany problem. Szybko, przyjemnie i łatwo.
Ale nie dodawaj po prostu includes w zapytaniu bez właściwego zrozumienia. Używanie includes z joins może skutkować połączeniami krzyżowymi w zależności od sytuacji, a w większości przypadków nie jest to potrzebne.
Jeśli chcesz dodać warunki do dołączonych modeli, musisz wyraźnie się do nich odnieść . Na przykład:
User.includes(:posts).where('posts.name = ?', 'example')
Zgłosi błąd, ale to zadziała:
User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
Zauważ, że includes działa z association names podczas gdy references potrzebuje the actual table name .