--- 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
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up