【问题标题】:Rails - eliminating n + 1 queries without table joinsRails - 在没有表连接的情况下消除 n + 1 个查询
【发布时间】:2015-11-30 02:24:21
【问题描述】:

我有一些 n + 1 个查询使我的网站的加载时间非常慢。这是一个类似于 Facebook 的社交媒体网站,n + 1 个查询的来源是朋友。详情 -

Friendships 表有 3 列 - User_id(发送请求的用户)、friend_id(收到请求的用户)和 pending,它是一个布尔值,表示友谊是否被接受。

好友请求是在我的用户模型中使用

实现的
def friend_requests
  friend_ids = Friendship.where('friend_id = ? AND pending = true', self.id).all
end

我特别需要友谊 ID,因为如果用户想要接受或拒绝请求,我需要更新待处理的布尔值。

Friendship 模型声明了一个 belongs_to => 朋友关联。

belongs_to :friend,
  class_name: "User",
  foreign_key: :friend_id,
  primary_key: :id

n+1 的来源。对于视图,当我想获取用户收到的好友请求时,我还想包含发送请求的用户的姓名和头像。它看起来像这样 -

json.friend_requests @user.friend_requests.includes(friend: :name) do |friendship|
  json.extract! friendship, :id, :user_id
  json.name User.find(friendship.user_id).name # n + 1
  json.profile_pic User.find(friendship.user_id).profile_pic # n + 1
end

我最初对 include(friend: :name) 的语法有一些疑问,但我已经尝试了this thread 中提到的所有排列,但它给了我

在用户上找不到名为“名称”的关联;也许你拼错了 是吗?

这是正确的,因为 name 是 User 的属性而不是关联。

我能想到的解决这个问题的唯一方法是将我的friend_requests 表更改为如下所示的find_by_sql 查询 -

SELECT f.id, f.user_id, f.friend_id, f.pending, users.name, users.profile_pic
FROM friendship AS f
JOIN users ON users.id = friendship.friend_id
WHERE f.friend_id = ? AND pending = true

它会给我用户的名字和 profile_pic,但我不想这样做,因为它看起来很脏,但更重要的是我想知道是否有更好、更智能的方法来做到这一点。

【问题讨论】:

标签: ruby-on-rails performance caching activerecord


【解决方案1】:

来自#includes 上的 API 文档:

指定要包含在结果集中的关系。

关键字是relationships:您指定要包含的整个关系,而不是字段。

你应该试试这个:

@user.friend_requests.includes(:friend) do |friendship|

  # whatever you need before

  # access the friend's name through the friend relationship
  # this should not require another query as the friend object was
  # preloaded into friendship using #includes
  friendship.friend.name

  # whatever you need after

end

编辑:实际上,这将触发 2 个 SQL 查询,第一个获取用户的 friend_requests,然后第二个同时获取所有 friend 关系。 第二个查询的形式是

SELECT "users".* FROM "users" WHERE "users"."id" IN (/*LIST OF IDs*/)

使用在第一个 friend_requests 查询中找到的所有 ID。

如果您只想触发一个查询,您应该尝试JOIN,可能是这样的:

@user.friend_requests.joins(:friend).includes(:friend)

【讨论】:

    猜你喜欢
    • 2012-03-25
    • 1970-01-01
    • 2021-11-12
    • 1970-01-01
    • 2021-03-28
    • 2012-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多