[英]Ruby's .where vs. detect
我正在尋找一種更快且使用更少服務器處理的方法。 在我的應用程序,我可以同時使用.where
和.detect
:
哪里:
User.where(id: 1)
# User Load (0.5ms)
檢測:
User.all.detect{ |u| u.id == 1 }
# User Load (0.7ms). Sometimes increases more than .where
我知道.detect
返回列表中塊返回 TRUE 的第一項,但是如果我有數千個用戶,它與.where
相比如何?
為清楚起見進行了編輯。
在這個例子中使用.where
是因為我可能不會單獨查詢id
。 如果我有一個名為“name”的表列怎么辦?
在這個例子中
User.find(1) # or
User.find_by(id: 1)
將是最快的解決方案。 因為這兩個查詢都告訴數據庫只返回一個具有匹配id
記錄。 一旦數據庫找到匹配的記錄,它就不會進一步查找,而是立即返回該記錄。
而
User.where(id: 1)
將返回匹配條件的對象數組。 這意味着:在找到匹配的記錄后,數據庫將繼續查找其他記錄以匹配查詢,因此始終掃描整個數據庫表。 在這種情況下——因為id
很可能是一個具有唯一值的列——它會返回一個只有一個實例的數組。
與
User.all.detect { |u| u.id == 1 }
這將從數據庫中加載所有用戶。 這將導致將數千個用戶加載到內存中,構建 ActiveRecord 實例,遍歷該數組,然后丟棄所有不符合條件的記錄。 與僅從數據庫加載匹配記錄相比,這將非常慢。
數據庫管理系統經過優化以運行選擇查詢,您可以通過設計有用的模式和添加適當的索引來提高他們這樣做的能力。 從數據庫加載的每條記錄都需要轉換為 ActiveRecord 的一個實例,並且會消耗內存——這兩種操作都不是免費的。 因此,經驗法則應該是:盡可能直接在數據庫中而不是在 Ruby 中運行查詢。
NB One 應該在這種特殊情況下使用ActiveRecord#find
,請參考@spickermann 的回答。
User.where
在 DB 級別執行,返回一條記錄。
User.all.detect
會將所有記錄返回給應用程序,然后才在 ruby 級別迭代。
也就是說,必須使用where
。 前者對大量記錄有抵抗力,可能有數十億條,執行時間/內存消耗幾乎相同( O(1)
。)后者甚至可能在數十億條記錄上失敗。
以下是一般指南:
每當您尋找唯一記錄時,請使用.find(id)
。 在搜索非 ID 字段時,您可以使用類似.find_by_email(email)
或.find_by_name(name)
或類似的東西(這些查找器方法是自動生成的),只要只有一條記錄具有該特定值。
如果您的查詢對於 .find_by 查詢來說太復雜,或者您需要使用排序但您仍然確定您只希望返回一條記錄,請使用 .where .where(...).limit(1)
。
檢索多條記錄時使用.where(...)
。
僅在無法避免時才使用.detect
。 .detect 的典型用例是在非 ActiveRecord 枚舉上,或者當您有一組記錄但無法在 SQL 中編寫匹配條件時(例如,如果它涉及復雜的函數)。 由於.detect
是最慢的,因此請確保在調用.detect
之前您已使用 SQL 盡可能縮小查詢范圍。 .any?
和其他可枚舉的方法。 僅僅因為它們可用於 ActiveRecord 對象並不意味着它們是一個好主意;)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.