优化 ActiveRecord 查询

Posted

技术标签:

【中文标题】优化 ActiveRecord 查询【英文标题】:Optimizing an ActiveRecord query 【发布时间】:2015-11-25 11:57:10 【问题描述】:

我有以下模型,并希望获取特定用户尚未在其上点赞的所有可用帖子。

后模型:

class Post < ActiveRecord::Base
  has_many :likes
  has_many :users, through: :likes

  scope :available, ->  where available: true 
end

喜欢模特:

class Like < ActiveRecord::Base
  belongs_to :post
  belongs_to :user
end

用户模型:

class User < ActiveRecord::Base
  has_many :likes
  has_many :posts, through: :likes
end

我想出了这个 ActiveRecord 查询:

Post.available - Post.available.joins(:likes).where('likes.user_id = ?', user.id)

有没有优化的方法来实现这一点?甚至可能是等效的 SQL 查询?

【问题讨论】:

创建Post.available.joins(:likes).where('likes.user_id = ?', user.id)的范围,然后减去 【参考方案1】:

这可以通过以下方式实现:

Post.available.where("id not in (select post_id from likes where user_id = ?)", user.id)

【讨论】:

【参考方案2】:

我猜这可以做到:

Post.available.where( 'NOT EXISTS (?)',
  user.likes.where('likes.post_id = posts.id')
)

您也可以完全不使用 SQL 字符串来执行此操作,如下所示:

Post.available.where(
  user.likes.where(post_id: Post.arel_table[:id]).exists.not
)

但我已经看到像这样的构造会导致参数绑定出现问题。我很确定这是一个错误,但我不确定这个技巧是否存在于公共 ActiveRecord API 中(ActiveRecord/Arel 真的应该受到责备)。

【讨论】:

【参考方案3】:

试试这个:-

Post.available.joins(:likes).where('likes.user_id NOT IN (?)', user.id)

或者您可以在后期模型中添加范围

class Post < ActiveRecord::Base
    scope :no_user_likes, ->(id)  joins(:likes).where('likes.user_id NOT IN (?)', id) 
end

然后你可以这样调用:-

Post.available.no_user_likes(user.id)

【讨论】:

与(现已正确删除)第一个答案的问题相同:这会获取 除此用户之外的所有人 喜欢的帖子。所以最终结果是错误的。

以上是关于优化 ActiveRecord 查询的主要内容,如果未能解决你的问题,请参考以下文章

使用过滤器对 Active Record 对象进行分页的优化方法是啥?

动态 Active Record 查询中的 Codeigniter 括号

CodeIgniter Active Record 查询 WITH/ 子查询

Rails 和 Active Record:复杂的 SQL 查询

如何在 Active Record 迁移中为字段添加索引以加快查询速度?

SQL 基准测试:PHP ActiveRecord ORM 与 MySQL 与 CodeIgniter Active Record 与标准 PHP