在自连接表中的两个外键之间搜索的查询

问题描述

用户表中的示例数据

image here

rails 控制台中的预期结果,father.childrenmother.children 应返回同一组对象:

father = User.first
father.children
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 3,name: "son",father_id: 1,mother_id: 2>,#<User id: 4,name: "daughter",mother_id: 2>]>
mother = User.find(2)
mother.children
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 3,mother_id: 2>]>

这是我现在在 User.rb 模型中的关联。如果我执行 father.children,它会返回预期的结果,因为foreign_key 指的是 father_id。但它不适用于 mother.children,因为它不引用 mother_id

 has_many :children,foreign_key: "father_id",class_name: "User"
 belongs_to :mother,class_name: "User",optional: true
 belongs_to :father,optional: true

有办法吗

foreign key: "father_id" OR "mother_id"

我也尝试在关联中进行查询,但似乎无法理解。

 has_many :children,-> { where("father_id = id OR mother_id = id") },class_name: "User"

解决方法

您可以取消该查询创建的默认范围,然后使用用户 ID 过滤子行:

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

为什么是unscope?如果不删除它,您的代码将导致两个 SQL 查询,如下所示:

SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT",1]]
SELECT "users".* FROM "users" WHERE "users"."user_id" = ? AND "users"."father_id" = ?  [["user_id",1],["father_id","1"]]

执行 User.first.children 时 - 至少在 Rails 6.1.4 中。

如您所见,第二个查询过滤所有用户,id 等于接收者 (1),同时 father_id 列等于接收者 (1)。看看你的代码,可能永远不会发生。

请注意,如果您在声明 has_many 关系后并不真的需要将所有这些方法添加到模型中,则最好使用单个实例方法或作用域(在最后一种情况下)。因为使用这种方法,您会覆盖默认行为(无处记录),即通过该表中的主键过滤另一个表中的行。