Preload
Этот метод загружает ассоциации в отдельном запросе:
User.preload(:posts).to_a # => SELECT "users".* FROM "users" SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1)
Т.к. preload всегда создает два отдельных запроса, то мы не можем использовать таблицу posts в условии выборки:
User.preload(:posts).where("posts.desc='ruby is awesome'") # => SQLite3::SQLException: no such column: posts.desc: SELECT "users".* FROM "users" WHERE (posts.desc='ruby is awesome')
А таблицу users – можем:
User.preload(:posts).where("users.name='Neeraj'") # => SELECT "users".* FROM "users" WHERE (users.name='Neeraj') SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (3)
Includes
По умолчанию includes действует точно так же, как и preload, но в случае наличия условия по ассоциированной таблице переключается на создание единственного запроса с LEFT OUTER JOIN.
User.includes(:posts).where('posts.desc = "ruby is awesome"').to_a # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3 FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE (posts.desc = "ruby is awesome")
Если по каким-то причинам необходимо форсировать применение такого подхода, то можно использовать метод references:
User.includes(:posts).references(:posts).to_a # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3 FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
Eager_load
Этот метод загружает ассоциации в одном запросе с использованием Left Outer Join, точно так же, как действует includes в сочетании с references.
User.eager_load(:posts).to_a # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3 FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
Joins
Создает запрос с использованием INNER JOIN.
User.joins(:posts) # => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
При этом, загружаются данные только из таблицы users. Кроме того, этот запрос может возвратить дублирующие друг друга записи:
def self.setup User.delete_all Post.delete_all u = User.create name: 'Neeraj' u.posts.create! title: 'ruby', desc: 'ruby is awesome' u.posts.create! title: 'rails', desc: 'rails is awesome' u.posts.create! title: 'JavaScript', desc: 'JavaScript is awesome' u = User.create name: 'Neil' u.posts.create! title: 'JavaScript', desc: 'Javascript is awesome' u = User.create name: 'Trisha' end
Результат выполнения User.joins(:posts) в БД с такими данными:
#<User id: 9, name: "Neeraj"> #<User id: 9, name: "Neeraj"> #<User id: 9, name: "Neeraj"> #<User id: 10, name: "Neil">
Избежать повторений мы можем с использованием distinct:
User.joins(:posts).select('distinct users.*').to_a
Если же мы хотим дополнительно получить какие-либо данные из таблицы posts, мы должны внести их в предложение select:
records = User.joins(:posts).select('distinct users.*, posts.title as posts_title').to_a records.each do |user| puts user.name puts user.posts_title end
Стоит заметить, что после выполнения метода joins вызов user.posts приведет в созданию еще одного запроса.
ссылка на оригинал статьи http://habrahabr.ru/post/191762/
Добавить комментарий