急切地加载主动记录关联时避免双重查询?

问题描述 投票:0回答:2

为了进行单个DB查询,我渴望加载帖子及其翻译数据(使用Rails 6和Mobility(*)),但它产生了2个SQL查询:

# app/models/post.rb
class Post < ApplicationRecord
  extend Mobility
  translates :title, :description, backend: :table
end

# app/controllers/posts_controller.rb
class PostsController < ApplicationRecord
  def index
    @posts = Post.eager_load(:translations).limit(3)
  end
end
<%# app/views/posts/index.html.erb %>
<% @posts.each do |post| %>
  <h1><%= post.title %></h1>
  <div><%= post.description %></div>
<% end %>

恢复:

    首先,选择所有帖子ID
  • 然后返回所有具有这些ID的帖子的属性
  • SELECT DISTINCT "posts"."id" FROM "posts" LEFT OUTER JOIN "post_translations" ON "post_translations"."post_id" = "posts"."id" LIMIT $1 [["LIMIT", 3]] SELECT "posts"."id" AS t0_r0, "posts"."created_at" AS t0_r1, "posts"."updated_at" AS t0_r2, "post_translations"."id" AS t1_r0, "post_translations"."locale" AS t1_r1, "post_translations"."created_at" AS t1_r2, "post_translations"."updated_at" AS t1_r3, "post_translations"."title" AS t1_r4, "post_translations"."description" AS t1_r5, "post_translations"."post_id" AS t1_r6 FROM "posts" LEFT OUTER JOIN "post_translations" ON "post_translations"."post_id" = "posts"."id" WHERE "posts"."id" IN ($1, $2, $3) [["id", "00060a7d-b846-5fc5-a372-1fc3462c695c"], ["id", "008db504-6fb4-5e90-bdca-4293ebe6d920"], ["id", "034944c1-4067-5ae5-89aa-4777ef14d66b"]]
如何避免使用ID内存的Double SQL语句?

(*)笔记 Mobility文档

具有产生单个SQL语句的示例,但是正如Chris Salzberg所指出的那样,在此示例中根本不使用其查询API,因此不应是罪魁祸首。试图证明该问题可能与移动性无关,而是主动记录本身,以下是一个与移动性相同的代码,这显示了相同的双重问问题(NB:这只是出于演示目的,就像我一样想要继续使用移动性):

class Post < ApplicationRecord has_many :translations, ->{ where(locale: I18n.locale) } %i(title description).each do |attr| define_method(attr) do translations.first.send(attr) end end class Translation < ApplicationRecord; end end <%# app/views/posts/index.html.erb %> <% Post.eager_load(:translations).limit(3).each do |post| %> <h1><%= post.title %></h1> <div><%= post.description %></div> <% end %>

如果您试图从收藏中获取一些非常具体的属性,那么CollectionProxy将为您提供单个查询。如果没有提供任何属性(列),则在执行

OUTER JOIN
查询之前进行不同的查询。
,我还没有阅读整个实施以确认其背后的推理。
但让我向你展示一件事。
2.5.1 :037 > Post.eager_load(:translations).each do |post| 2.5.1 :038 > puts post.title 2.5.1 :039?> end SQL (0.5ms) SELECT "posts"."id" AS t0_r0, "posts"."title" AS t0_r1, "posts"."created_at" AS t0_r2, "posts"."updated_at" AS t0_r3, "post_translations"."id" AS t1_r0, "post_translations"."title" AS t1_r1, "post_translations"."content" AS t1_r2, "post_translations"."locale" AS t1_r3, "post_translations"."post_id" AS t1_r4, "post_translations"."created_at" AS t1_r5, "post_translations"."updated_at" AS t1_r6 FROM "posts" LEFT OUTER JOIN "post_translations" ON "post_translations"."post_id" = "posts"."id" title 1 title 2 title 3 title 4
ruby-on-rails ruby activerecord eager-loading mobility
2个回答
2
投票
在上述情况下,您可以看到eager_load可以执行您的期望。我认为,在懒惰评估时,您认为它不提出所需属性的类似情况,除了查询

distinct

希望这有帮助。如果有任何评论,请发布,以便我可以澄清一下。 :)

如果您以某种方式迫使Rails在单个查询中运行它,那么您将不喜欢后果。

由于
OUTER JOIN

2.5.1 :040 > Post.eager_load(:translations) SQL (0.3ms) SELECT DISTINCT "posts"."id" FROM "posts" LEFT OUTER JOIN "post_translations" ON "post_translations"."post_id" = "posts"."id" LIMIT ? [["LIMIT", 11]] SQL (0.5ms) SELECT "posts"."id" AS t0_r0, "posts"."title" AS t0_r1, "posts"."created_at" AS t0_r2, "posts"."updated_at" AS t0_r3, "post_translations"."id" AS t1_r0, "post_translations"."title" AS t1_r1, "post_translations"."content" AS t1_r2, "post_translations"."locale" AS t1_r3, "post_translations"."post_id" AS t1_r4, "post_translations"."created_at" AS t1_r5, "post_translations"."updated_at" AS t1_r6 FROM "posts" LEFT OUTER JOIN "post_translations" ON "post_translations"."post_id" = "posts"."id" WHERE "posts"."id" IN (?, ?, ?, ?) [["id", 1], ["id", 2], ["id", 3], ["id", 4]] => #<ActiveRecord::Relation [#<Post id: 1, title: nil, created_at: "2021-07-08 12:42:13", updated_at: "2021-07-09 15:32:48">, #<Post id: 2, title: nil, created_at: "2021-07-09 15:33:50", updated_at: "2021-07-09 15:33:50">, #<Post id: 3, title: nil, created_at: "2021-07-09 15:33:55", updated_at: "2021-07-09 15:33:55">, #<Post id: 4, title: nil, created_at: "2021-07-09 15:33:57", updated_at: "2021-07-09 15:33:57">]>

,铁轨设法避免了
DISTINCT "posts"."id"
。拥有最后一个条件将迫使数据库完全比较记录,而不仅仅是主要键!

我100%确定:
DISTINCT "posts"."*" 

在引擎盖下,除非您跳过相应的索引,否则此请求恰好是索引。

0
投票

distinct.id:

固定。*:

    

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.