【问题标题】:SQL Query to find records having certain records in a has_many through relationshipSQL 查询以通过关系查找在 has_many 中具有某些记录的记录
【发布时间】:2019-07-19 17:44:15
【问题描述】:

我有以下型号:

# contributor.rb

class Contributor < ApplicationRecord
  has_many :skill_groupings
  has_many :skills, through: :skill_groupings
end

# skill_grouping.rb

class SkillGrouping < ApplicationRecord
  belongs_to :skill
  belongs_to :contributor
end

# skill.rb

class Skill < ApplicationRecord
  has_many :skill_groupings
  has_many :contributors, through: :skill_groupings
end

我想搜索(SQL 查询)其技能 ID 包含所有数组中包含的值的贡献者。

假设数据如下:

contributor_1.skill_ids: [2, 3, 6, 7]
contributor_2.skill_ids: [4, 7]
contributor_3.skill_ids: [9]
contributor_4.skill_ids: []

wanted_ids = [3, 7]

Contributor.joins(:skill_groupings).where(skill_groupings: { skill_id: wanted_ids }).distinct 返回技能 ID 中有 3 个 7 的贡献者,因此是[contributor_1, contributor_2]。这不是我想要的。

查询的结果应该是[contributor_1],因为只有contributor_1拥有wanted_ids中的所有技能ID。

很遗憾,skill_ids 似乎只是 Rails 引入的一个属性,在 SQL 中不可用。

我怎样才能仅通过 SQL 查询来实现这一点?

【问题讨论】:

  • Contributor.where(id: Contributor.select(:id).joins(:skills).where(skills: { id: wanted_ids }).group(:id).having("COUNT(skills.id) = #{wanted_ids.uniq.size}")) 对你有用吗
  • @engineersmnky 是的,谢谢,但这不是太长太麻烦了吗?是否存在更简单的查询?
  • IMO 这是实现您想要做的最简单的方法,这就是我在纯 SQL 中的处理方式(这会更长)。由于这是 postgres,因此以下内容应该可以工作并将其缩短一点Contributor.joins(:skills).where(skills: { id: wanted_ids }).group(:id).having("COUNT(skills.id) = #{wanted_ids.uniq.size}"))。这是因为 postgres 允许您在返回所有列的同时按标识列分组(在大多数其他 SQL 数据库中不起作用)
  • @engineersmnky 如果你写你的评论作为答案,我会接受它

标签: sql ruby-on-rails postgresql


【解决方案1】:

假设一个贡献者不能多次拥有相同的技能,我能看到的最简单的方法是找到所有具有这些技能的贡献者,这些贡献者的技能数量与传入的技能相同,例如

Contributor.joins(:skills)
  .where(skills: { id: wanted_ids })
  .group(:id)
  .having("COUNT(skills.id) = #{wanted_ids.uniq.size}"))

这将导致以下 SQL 给出wanted_ids = [3, 7]

SELECT 
  contributors.*
FROM 
  contributors 
  INNER JOIN skill_groupings ON skill_groupings.contributor_id = contributors.id
  INNER JOIN skills ON skills.id = skill_groupings.skill_id
WHERE
  skills.id IN (3,7)
GROUP BY 
  contributors.id 
HAVING 
  COUNT(skills.id) = 2

这仅适用于 PostgreSQL,因为此 SQL 服务器允许按主键进行分组,同时仍选择整行列。对于其他数据库,您将需要一个子查询,例如:

Contributor.where(id: 
  Contributor.joins(:skills)
    .select(:id)
    .where(skills: { id: wanted_ids })
    .group(:id)
    .having("COUNT(skills.id) = #{wanted_ids.uniq.size}")
)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-29
    相关资源
    最近更新 更多