SQL查询通过关系在has_many中查找具有某些记录的记录

时间:2019-07-19 17:44:15

标签: sql ruby-on-rails postgresql

我有以下型号:

# 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查询)其skill_ids包含所有包含在数组中的值的贡献者。

假设数据如下:

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查询来实现这一目标?

1 个答案:

答案 0 :(得分:0)

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

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

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

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 Server允许按主键进行分组,同时仍选择整个列。对于其他数据库,您将需要一个子查询,例如:

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