Rails Rails 3 中生 MySQL subquery 的秘技

xdite · 2013年05月07日 · 最后由 doitian 回复于 2013年05月07日 · 4895 次阅读
本帖已被管理员设置为精华贴

http://blog.rocodev.com/posts/13-mysql-subquery-in-rails-3

有時候設計 Rails Application 時,某些 JOIN 的語法並不是那麼好寫。(不管是手刻,或者是透過 ORM 下出來。)

所以在 Application 層(PHP or Rails) 下兩次 SELECT 或者是用 subquery 往往會變成直覺下的取代方案。

舉個例子,有個功能叫做:我上架的書有被人 facvorite 的總數量。

一般開發者在不用 JOIN 的情況下,會寫出這樣的 code

book_ids = Book.where(:user_id => user.id)
favorite_count = Favorite.where(:book_id => book_ids).select("book_id").map(&:book_id).uniq.size

因為你無法知道 book_ids 怎麼拉出來丟 query,只好拉到 Application 層再塞回去用 IN 撈。數量小這樣的作法是還 okay….但…萬一 book_ids 多達萬,顯然這招就效了。除了慢之外,還製造一堆無用物件浪費記憶體空間…

而 Rails 3 背後強大的 Arel,其實提供了自動算出 subquery 的語法:

其實上面那一段可以改為這樣寫

favorite_count = Favorite.where(:book_id => Book.where(:user_id => user.id)).select("distinct book_id").count

是的,在查詢語句裡面再下條件就會自動變成 subquery 了....

Favorite.where(:book_id => Book.where(:user_id => user.id))

http://stackoverflow.com/questions/5483407/subqueries-in-activerecord

幾個原則

  • 不是 JOIN 就是慢
  • 兩個 SELECT 不一定比 JOIN 快。(在以上 book_id 數量很大的 case 中)
  • 兩個 SELECT 有時候會比 JOIN 快。(在以上 book_id 數量很小的 case 中)
  • 有時候應該用 SUBQUERY 取代兩個 SELECT
  • SUBQUERY 不是萬靈丹,因為大多時候 JOIN 蠻多時候比 SUBQUERY 有效率,但 SUBQUERY 好寫蠻多的,可偷吃步…
  • SUBQUERY 適可而止,太多層 SUBQUERY 效能掉很兇(兩層其實就慢了...)。

昨天用這招 refactor 掉一個 2220ms 的 query ...XD

favorite_count ...

用 redis 更自然。

不错 不过习惯用 joins~

很有启发

学习了。。

学习。 另外这句:

除了慢之外,還製造一堆無用物件浪費記憶體空間…

除了慢以外,还浪费内存 的意思么?

计数怎么不直接 count(:x, unique: true)?

需要 登录 后方可回复, 如果你还没有账号请 注册新账号