Mik*_*H-R 7 activerecord ruby-on-rails
例:
Foobar.joins(:baz).includes(:baz).count
=> 22926
Foobar.joins(:baz).includes(:baz).find_each.count
=> 998
Foobar.joins(:baz).find_each.count
=> 22926
Run Code Online (Sandbox Code Playgroud)
在正确的情况下生成的sql(第三个)是几批sql,如下所示:
SELECT "foobar".* FROM "foobar" INNER JOIN "baz" ON
"baz"."foobar_id" = "foobar"."id" ORDER BY "foobar"."id" ASC LIMIT $1
Run Code Online (Sandbox Code Playgroud)
在失败的(第二个)情况下,有一个看起来像的查询:
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
Run Code Online (Sandbox Code Playgroud)
其中所有字段都被列为t0_r0每个表上不同列的不同临时变量(例如)(在实际查询中,第一个对象上有37个分割30,第二个对象上有7个).
这是一个错误吗?都includes在一个不允许find_each查询?难道我做错了什么?
Foobar和之间的关系Baz是Foobar has_one Baz和Baz belongs_to Foobar.
如果你的has_one关系不是真的会发生这个问题has_one。
假设您的数据库在该列上没有唯一索引baz.foobar_id。然后,您可能会意外地遇到这样的情况,其中您有一个Foobar记录连接到多个Baz记录:
baz.id | baz.foobar_id
------ -------------
1 1
2 1
3 2
Run Code Online (Sandbox Code Playgroud)
在这种情况下,joins将会返回Foobar和Baz记录的组合:
Foobar.joins(:baz).count # This would be 3
Run Code Online (Sandbox Code Playgroud)
这也意味着find_eachwith join将迭代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
Run Code Online (Sandbox Code Playgroud)
添加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
Run Code Online (Sandbox Code Playgroud)
并且此时find_each将停止处理,因为它发现早期批处理小于批处理大小,因此认为已完成:
# ActiveRecord::Batches#in_batches
break if ids.length < batch_limit
Run Code Online (Sandbox Code Playgroud)
默认的批处理大小为find_each1,000。您的问题案例返回了998条记录。这表明第一批加载了998个唯一的Foobar ID,该ID小于批处理大小,并find_each认为已完成。它可能加载了1,000条Baz记录,这些记录与998条不同的Foobar记录相关。
您可能需要查看baz表以查看表中是否有重复的条目。您可以使用以下方法执行此操作:
Baz.group(:foobar_id).having('count(*) > 1')
Run Code Online (Sandbox Code Playgroud)
最好的解决方案是使用唯一索引,以避免数据库中出现重复并加强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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2134 次 |
| 最近记录: |