TODO N+1
Introduction
Ruby on Rails ORM has lazy loading enabled.
Example
# Post Model
class Post < ActiveRecord::Base
belongs_to :author
end
# Author Model
class Author < ActiveRecord::Base
has_many :posts
end# Controller
class PostsController < ApplicationController
def index
@posts = Post.order(created_at: :desc)
end
end# View
<% @posts.each do |post| %>
<td><%= post.title %></td>
<td><%= post.author.name %></td>
<% end %>authors = Author.create([{ name: 'John' }, { name: 'Doe' }, { name: 'Manish' }])
Post.create(title: 'I love Tuts+', body: '', author: authors.first)
Post.create(title: 'Tuts+ is Awesome', body: '', author: authors.second)
Post.create(title: 'Long Live Tuts+', body: '', author: authors.last)Case 1: Default - lazy loading
@posts = Post.order(created_at: :desc)N+1
Post Load (0.6ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC
Author Load (0.5ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT 1 [["id", 3]]
Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT 1 [["id", 2]]
Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT 1 [["id", 1]]Case 2.1: preload - eager loading / separated queries
preload - eager loading / separated queries@posts = Post.order(created_at: :desc).preload(:author)SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC
SELECT "authors".* FROM "authors" WHERE "authors"."id" IN (3, 2, 1)No N+1
Fail @ order posts by author name
Fail @ find posts by author name
Case 2.2: eager_load - eager loading / one query
eager_load - eager loading / one query@posts = Post.order(created_at: :desc).eager_load(:author)SELECT "posts"."id" AS t0_r0, "posts"."title" AS t0_r1, "posts"."body" AS t0_r2, "posts"."author_id" AS t0_r3, "posts"."created_at" AS t0_r4, "posts"."updated_at" AS t0_r5, "authors"."id" AS t1_r0, "authors"."name" AS t1_r1, "authors"."created_at" AS t1_r2, "authors"."updated_at" AS t1_r3
FROM "posts"
LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
ORDER BY "posts"."created_at" DESCorder posts by author name
@posts = Post.order("authors.name").eager_load(:author)SELECT "posts"."id" AS t0_r0, "posts"."title" AS t0_r1, "posts"."body" AS t0_r2, "posts"."author_id" AS t0_r3, "posts"."created_at" AS t0_r4, "posts"."updated_at" AS t0_r5, "authors"."id" AS t1_r0, "authors"."name" AS t1_r1, "authors"."created_at" AS t1_r2, "authors"."updated_at" AS t1_r3
FROM "posts"
LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
ORDER BY authors.namefind posts by author name
@posts = Post.order(created_at: :desc).eager_load(:author).where("authors.name = ?", "Manish")SELECT "posts"."id" AS t0_r0, "posts"."title" AS t0_r1, "posts"."body" AS t0_r2, "posts"."author_id" AS t0_r3, "posts"."created_at" AS t0_r4, "posts"."updated_at" AS t0_r5, "authors"."id" AS t1_r0, "authors"."name" AS t1_r1, "authors"."created_at" AS t1_r2, "authors"."updated_at" AS t1_r3
FROM "posts"
LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
WHERE (authors.name = 'Manish')
ORDER BY "posts"."created_at" DESCsingle query and LEFT OUTER JOINcan also be very expensive.
Case 2.3 includes - smarter
includes - smarter(same as preload)
@posts = Post.order(created_at: :desc).includes(:author)SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC
SELECT "authors".* FROM "authors" WHERE "authors"."id" IN (3, 2, 1)order posts by author name (same as eager_load)
@posts = Post.order("authors.name").includes(:author)SELECT "posts"."id" AS t0_r0, "posts"."title" AS t0_r1, "posts"."body" AS t0_r2, "posts"."author_id" AS t0_r3, "posts"."created_at" AS t0_r4, "posts"."updated_at" AS t0_r5, "authors"."id" AS t1_r0, "authors"."name" AS t1_r1, "authors"."created_at" AS t1_r2, "authors"."updated_at" AS t1_r3
FROM "posts"
LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
ORDER BY authors.namefind posts by author name (same as eager_load)
@posts = Post.order(created_at: :desc).includes(:author).where("authors.name = ?", "Manish").references(:author)SELECT "posts"."id" AS t0_r0, "posts"."title" AS t0_r1, "posts"."body" AS t0_r2, "posts"."author_id" AS t0_r3, "posts"."created_at" AS t0_r4, "posts"."updated_at" AS t0_r5, "authors"."id" AS t1_r0, "authors"."name" AS t1_r1, "authors"."created_at" AS t1_r2, "authors"."updated_at" AS t1_r3
FROM "posts"
LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
WHERE (authors.name = 'Manish')
ORDER BY "posts"."created_at" DESCLast updated
Was this helpful?