--- title: Rails 中 includes 跟 joins 的差別 tags: published_on_blog, note, rails --- # Rails 中 `includes` 跟 `joins` 的差別 :::warning :film_projector: 前情提要 最近幫忙在調校網站 n+1 的問題,所以想來寫個筆記,分成兩篇。 1. [Rails 中 `includes` 跟 `joins` 的差別](/q_rNI_PdS2umYDnVTx4x0A) 2. [Rails 的 `length`、`count`、`size` 差別?](/DudAkB-iRPiNOzt7AyzEBA) ::: ## 何謂 `N+1` 問題 在 Rails 中建立 model 的資料關聯十分的容易,但也是因為建立方便,所以很容易就會寫出效能差的程式碼,每一次進出 Database 都會消耗很大的效能,到底何謂 `N+1` 問題呢? 舉個例子: ```ruby= # User has_many :orders #Order belongs_to :user ``` 當我們想在畫面上印出所有 `orders` 的 `user.email`,此時所造成的 `query` 就會長這樣:  上面的寫法就會先將所有 `orders`,找出來,在針對每一筆的 `order.user_id` 去查找`users`的表,共有 9 筆,所以找 9 次。 若有 n 筆就找 n 次,再加上一開始找出所有 `orders` 的 `query` ,就變成了 `n+1`。 若資料量稍微龐大一點,效能就會變差。 ## 請善用 `includes` 而 Rails 很貼心的有個語法可以讓我們避免這樣的問題,`includes`。 若將 `orders = Order.all` 改成 `orders = Order.all.includes(:user)`  那在最一開始撈出所有 order 時,會順便一起把相對應的 user 找出來,此時繼續輸入: ```ruby orders.each do |order| order.user.email end ``` 這時就不會進到 Database 中查詢。 從 n+1 變成 1+1 :+1: :+1: :+1: :+1: :+1: :+1: :+1: :+1: ### `includes` 也支援巢狀關聯 (使用 hash) 如果除了 `user` ,還想一起找出跟 `user` 有關聯的 model 可以使用 hash 的寫法 ```ruby= # 以 profile 為例 # User has_many profiles orders = Order.all.includes(user: [ :profile ]) # 可以順便把 profile 撈出來 ``` ## 還有一個語法叫 `joins` 除了 `includes` 之外,還有一個很像的語法叫做 `joins`,不過差別為 `joins` 是過濾 `model` 之間的關聯,像上方的寫法,在印出 `user.email` 的時候還是會產生查詢! ```ruby= orders = Order.all.joins(:user) # 回傳具有 user_id 的 order ``` #### 而 `has_many` 又會跟 `belongs_to` 的情境有不一樣的回傳結果 ```ruby= # User has_many :orders user = User.joins(:orders) ``` - 查詢所有具有 `user_id` 的 `order`,並回傳該 `order` 的 `user` ,若有很多 `order` 所屬同一個 `user`,則回傳值會包含大量同一名 `user` 的資料,可以用 `uniq` 處理。 ## 來個總結! 簡單來說,若是要處理大量資料,會比較建議使用 `includes`,以避免 n+1 的問題,而 `joins` 的話,目前推論是需要對資料做關聯 model 的條件篩選,才會比較建議使用。 ------ #### 參考文章 [Rails使用 include 和 join 避免 N+1 query](https://adlerhsieh.com/blog/20141028-rails-include-join-avoid-n-1-query) [Ruby on Rails 實戰聖經 - 網站效能](https://ihower.tw/rails/performance.html) [官方 API 文件](https://apidock.com/rails/ActiveRecord/QueryMethods/includes) [stackoverflow](https://stackoverflow.com/questions/1208636/rails-include-vs-joins)
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.