将 SQL 查询从关联切换到实例方法

Posted

技术标签:

【中文标题】将 SQL 查询从关联切换到实例方法【英文标题】:Switching SQL queries from association into instance methods 【发布时间】:2021-09-29 18:49:41 【问题描述】:

我当前的 User.rb 模型

class User < ApplicationRecord
  has_many :children,
           -> (user)  unscope(:where).where("father_id = :id OR mother_id = :id", id: user.id) ,
           class_name: "User"

  has_many :grandchildren,
           -> (user)  unscope(:where).where("father_id IN (:ids) OR mother_id IN (:ids)", ids: user.children.ids) ,
           class_name: "User"

  belongs_to :mother, class_name: "User", optional: true
  belongs_to :father, class_name: "User", optional: true
end

我当前的数据(空的为零):

所有查询现在都可以使用:

但对于.grandchildren 查询,您可以看到在控制台中创建了三个(两个重复查询)查询。有没有办法只生成一个查询?

我正在尝试实例方法,因为我可以输入原始 SQL,但似乎无法理解它。一个例子:

  def children
    sql = ("SELECT users.* FROM users WHERE (father_id = id OR mother_id = id)")
    p ActiveRecord::Base.connection.execute(sql)
  end

最终目标:

在执行.grandchildren 时生成一个查询

【问题讨论】:

【参考方案1】:

你可以替换

-> (user)  unscope(:where).where("father_id IN (:ids) OR mother_id IN (:ids)", ids: user.children.ids) ,

-> (user) do
  scope = unscope(:where)
  children_ids = user.children.select(:id)

  scope
    .where(father_id: children_ids)
    .or(scope.where(mother_id: children_ids))
end

【讨论】:

@engineersmnky 使用user.children.select(:id) 已经创建子查询而不加载记录:) 这是 #<:activerecord_relation:0x00007fca342d1350>) 的错误 NameError (undefined local variable or method childrens_id')` @engineersmnky 编辑后,错误为NameError (uninitialized constant User::Grandchild) @LeeRenJie 此答案中提供的代码的任何部分都不会导致该错误。我认为问题在于您正在尝试使用has_many,它将从关系中推断出类名。在这种情况下,尽管childrengrandchildren 都将受益于转换为scopes 而不是关系。 AR 关系应该是可连接的,并且由于连接的复杂性和您对实例 -&gt; (user) 的使用,这些关系中的任何一个都不能自然地在 ORM 中连接,这两者目前都不受 AR 提供的高级 DSL 的支持跨度> 【参考方案2】:

解决了这个

  has_many :children,
  -> (user)  unscope(:where).where("father_id = :id OR mother_id = :id", id: user.id) ,
   class_name: "User"

  belongs_to :mother, class_name: "User", optional: true
  belongs_to :father, class_name: "User", optional: true

  def grandchildren
    user_id = self.id
    User.where(
      "father_id IN (:ids) OR mother_id IN (:ids)",
      ids: User.where("father_id = :id OR mother_id = :id", id: user_id).pluck(:id)
    )
  end

【讨论】:

以上是关于将 SQL 查询从关联切换到实例方法的主要内容,如果未能解决你的问题,请参考以下文章

如何从 LocalDB 实例切换到 SQL Express?

求SQL多表查询公式。从A表查询结果去关联B表里面的字段

mysql通过“延迟关联”进行limit分页查询优化的一个实例

将大型 SQL 托管实例还原到本地

SQL Server 2012 AlwaysOn 高可用 主切换到辅(从)的时候,怎么在主的实例上做备份

将一行从 SQL 数据映射到 Java 对象