1

運行兩個類似的查詢,如不Rails的自動的優化查詢

@articles = @magazine.articles.limit(2).offset(0) 
@articles = @articles.limit(2).offset(2) 

,我期待看到我的控制檯兩個SQL語句由服務器執行之後。但是,第一個查詢丟失,只有第二個查詢正在運行。同樣,執行以下兩個查詢後:

@articles = @magazine.articles.limit(2).offset(0) 
@articles = @articles.limit(2).offset(@articles.size - 2) 

第一個查詢也被完全忽略。這兩個查詢生成SQL:

SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "articles" 
WHERE "articles"."magazine_id" = $1 LIMIT 2 OFFSET 0) 
subquery_for_count [["magazine_id", 1]] 

SELECT "articles".* FROM "articles" 
WHERE "articles"."magazine_id" = $1 
LIMIT 2 OFFSET 2 [["magazine_id", 1]] 

有趣的是,如果我改變@articles.size@articles.length兩種查詢按預期運行。我會認爲,因爲length需要在內存中收集,第一條語句被迫運行。任何人都可以描述這裏發生了什麼,如果它是一個太廣泛的話題,請將我指向一個好的資源。

回答

2

它並沒有像推遲執行查詢那麼多的優化,直到它真的需要執行它。

在這兩種情況下,您都會在@articles中存儲構建查詢的結果。活動記錄或更準確地說是arel,會推遲執行查詢,直到您調用需要結果的方法。我懷疑當你打電話給@artircles.each@articles.count或某些東西時,你實際上看到了對數據庫執行的查詢。

你可以在一系列步驟構建查詢並不會真正得到執行:

a = @magazine.articles 
a = a.limit(2) 
a = a.offset(0) 

這也意味着你可以留下一些大大降低了結果的大小,以結束查詢子句進程:

a = a.where('created_at > ?', Time.now.at_beginning_of_day) 

仍然沒有查詢已發送到數據庫。

要注意的是在rails控制檯中測試這個邏輯。如果你在控制檯本身運行這些步驟,它會嘗試顯示最後一個返回值(通過調用.inspect我認爲)並通過檢查返回值導致查詢被執行。因此,如果您將a = Magazine.find(1).articles放入控制檯中,您會看到立即執行的查詢,例如,如果代碼是在控制器操作的上下文中運行的,則不會執行該查詢。如果您然後撥打a.limit(2),您會看到另一個查詢等。

+0

至於它爲什麼這樣工作:聲明的結束無法確定。鏈中的每個方法都會返回先前構建的查詢的變體版本。但是沒有辦法確定一個方法是否是鏈中的最後一個,並且它是否應該獲取數據。呃......對於惰性迭代器,沒有像'force'這樣的方法嗎? –

+0

我對控制檯的信任確實讓我更加滿意。<。對延遲加載做了一些更多的閱讀,現在我更好地理解這個問題。非常感謝! – Steve