首页 > 解决方案 > Rails - 获取父ID不在任何子关联中的对象

问题描述

我有这两个模型:

Collabs
- id
- title
# has_many :collaborations

Collaborations
- collab_id
- user_id
- status
# belongs_to :collab
# belong_to :user

在查询中,我不想获取所有协作,其中某个 user_id 不存在于子(协作)关联中。一个协作可以有零到多个协作,并且协作将具有不同的 user_id。

我试过(使用范围):

collabs = Collab.available_for_user(2)

scope :available_for_user, -> (user_id) { joins(:collaborations).where.not(collaborations: {user_id: user_id}) }

我也试过:

scope :available_for_user, -> (user_id) { left_outer_joins(:collaborations).where.not(collaborations: {user_id: user_id}) }

这是在控制台中输出的 SQL:

SELECT "collabs".* FROM "collabs" LEFT OUTER JOIN "collaborations" ON "collaborations"."collab_id" = "collabs"."id" WHERE ("collaborations"."user_id" != $1)  [["user_id", 13]]

如果子关联仅具有提供的 user_id,但如果协作与另一个 user_id 有另一个协作,则此协作有效,则使用上述范围获取此协作。

标签: ruby-on-railsjoin

解决方案


这是你的电话:

Collab.find_by_sql [
  "SELECT *
  FROM collabs
  WHERE id NOT IN (
      SELECT C.id
      FROM collabs C
      JOIN collaborations CL ON CL.collab_id = C.id
      WHERE CL.user_id = :user_id)",
  { user_id: user_id }
]

说明:要获取所选 user_id 没有协作的协作,有必要找到具有协作的协作,并排除它们。:)

如果没有 SQL,使用普通的 Ruby,它可以写成:

Collab.where.not(
  id: Collab.joins(:collaborations)
            .where(collaborations: { user_id: user_id })
            .pluck(:id)
)

或者,更多细节:

# Inner query to find collabs with collaborations for given user_id:
#   SELECT C.id
#   FROM collabs C
#     JOIN collaborations CL ON CL.collab_id = C.id
#   WHERE CL.user_id = :user_id
ids = Collab.joins(:collaborations)
            .where(collaborations: { user_id: user_id })
            .pluck(:id)

# Final query:
#   SELECT *
#   FROM collabs
#   WHERE id NOT IN :ids
Collab.where.not(id: ids)

它产生两个 SQL 调用,而不是第一种情况下的一个(find_by_sql),所以为了性能起见,请避免这样做。


推荐阅读