JT's Blog

Do the right things and do the things right.

Using Joins and Includes to Avoid N+1 Query Problem

| Comments

就使用上來說,要從多筆資料去找 belongs_to 的資料,似乎是不太合邏輯

但熟悉使用 ORM 使用者,因為一切都被包裝太的好,或許就會忘記想到有這樣的問題

Model Relation

場景設定,使用者有多個Comments

1
2
3
4
5
6
7
8
9
# app/models/user.rb
class User < ActiveRecord::Base
  has_many :comments
end

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :user
end

觸發條件 (has_many)

取得所有的comments,並找出該 comment 的 user 的 email

之所以為 N+1 Query 就是一筆查所有comments,N 筆查每個comment.user.email

1
2
comments = Comment.all
comments.each { |comment| comment.user.email }

使用 Joins

joins

joins 使用 INNER JOIN 的方式查詢,結果只有comments

1
2
3
Comment.joins(:user)

# SELECT users.* FROM comments INNER JOIN users ON users.id = comments.user_id

使用 Includes

includes

includes 使用 WHERE IN 的方式查詢,同時會一併取得users 的資料

1
2
3
4
Comment.includes(:user)

# SELECT comments.* FROM comments
# SELECT users.* FROM users WHERE users.id IN (1)

反向思考:如果user 使用 joins / includes 會怎樣呢?

雖然都知道可以使用 user.comments 就可以得到答案,但就想實驗使用後的結果

  • joins

產生重複的 User 資料,得到得資料還得用 .uniq 刪除

1
User.joins(:comments)
  • includes

結果一樣,同時會一併取得 comments 的資料

1
User.includes(:comments)

Comments