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
.