2017-04-07 39 views
4

例如:您是否允許使用包含語句的find_each查詢?

Foobar.joins(:baz).includes(:baz).count 
=> 22926 
Foobar.joins(:baz).includes(:baz).find_each.count 
=> 998 
Foobar.joins(:baz).find_each.count 
=> 22926 

在正確的情況下(第三)生成的SQL是SQL,看起來像的幾個批次:

SELECT "foobar".* FROM "foobar" INNER JOIN "baz" ON 
"baz"."foobar_id" = "foobar"."id" ORDER BY "foobar"."id" ASC LIMIT $1 
在失敗

(第二)的情況下有一個單一的查詢看起來像:

SELECT "foobar"."id" AS t0_r0 
"baz"."id" AS t1_r0 
"baz"."foobar_id" AS t1_r1 
FROM "foobar" INNER JOIN "baz" ON "baz"."foobar_id" = "foobar"."id" 
ORDER BY "foobar"."id" ASC LIMIT $1 

其中所有字段都列爲關於電子的不同列的不同的臨時變量(例如t0_r0) ach表(在實際查詢中,第一個對象上有37個分割的30個,第二個對象上有7個)。

這是一個錯誤? includes不允許在find_each查詢中被允許嗎?難道我做錯了什麼?

FoobarBaz之間的關係是Foobarhas_oneBazBazbelongs_toFoobar

+0

你使用的是什麼Rails版本? –

+0

我正在使用Rails 5.0.2 –

回答

1

如果您的has_one關係不是真的has_one,則可能會發生此問題。

假定您的數據庫在列baz.foobar_id上沒有唯一索引。然後,你可能會不小心與這樣的情況下結束了,你必須連接到一個以上的巴茲記錄一個Foobar的記錄:

baz.id | baz.foobar_id 
------ ------------- 
1  1 
2  1 
3  2 

在這種情況下,joins將返回的Foobar和巴茲記錄的組合:

Foobar.joins(:baz).count # This would be 3 

這也意味着,find_eachjoin會重複3次,重複Foobar的ID之一:

Foobar.joins(:baz).find_each(batch_size: 2) { |f| puts f.id } 
# SELECT "foobar".* FROM "foobar" INNER JOIN "baz" ON... LIMIT 2 
1 
1 
# SELECT "foobar".* FROM "foobar" INNER JOIN "baz" ON... WHERE ("foobar"."id" > 1) ... LIMIT 2 
2 

includes中添加意味着Rails將嘗試將結果整合回一組不同的Foobar記錄中。但是,這不會怎麼find_each管理其批量工作:

Foobar.joins(:baz).includes(:baz).find_each(batch_size: 2) { |f| puts f.id } 
# SELECT "foobar"."id" AS t0_r0 ... LIMIT 2 
1 

而在這一點find_eachstop processing,因爲它發現了一個早期的批次比批量大小小,因此它認爲它是做:

# ActiveRecord::Batches#in_batches 
break if ids.length < batch_limit 

find_each的默認批量大小爲1,000。您的問題案例返回了998條記錄。這表明第一批它裝載了998個小於批量大小的獨特Foobar ID,find_each認爲它已完成。它可能裝載了1000個Baz記錄,這些記錄連接到998個不同的Foobar記錄。

您可能需要查看您的baz表以確定它是否有任何重複條目。你可以像這樣做:

Baz.group(:foobar_id).having('count(*) > 1') 

最好的解決辦法是使用一個唯一索引,以避免在數據庫中的重複和執行has_one關係。另一種方法是確保你得到一組類似的Foobar記錄:

Foobar.group(:id).joins(:baz).includes(:baz).count 
Foobar.group(:id).joins(:baz).includes(:baz).find_each.count 
Foobar.group(:id).joins(:baz).find_each.count 
相關問題