在has_many关联中选择所有包含某些条件的记录 - Ruby On Rails

Imr*_*qvi 8 postgresql activerecord ruby-on-rails ruby-on-rails-4

我有model profile.rb以下关联

class User < ActiveRecord::Base
   has_one :profile
end

class Profile < ActiveRecord::Base
    has_many :skills
    belongs_to :user
end
Run Code Online (Sandbox Code Playgroud)

我有model skills.rb以下关联

class Skill < ActiveRecord::Base
    belongs_to :profile
end
Run Code Online (Sandbox Code Playgroud)

我在技能表中有以下条目

id:         name:           profile_id:
====================================================
1           accounting          1
2           martial arts        2
3           law                 1
4           accounting          2
5           journalist          3
6           administration      1
Run Code Online (Sandbox Code Playgroud)

等等,我如何查询所有配置文件,假设"会计"和"管理"技能,这将是id为1的配置文件,考虑到上述重新编码.到目前为止,我试过以下

Profile.includes(:skills).where(skills: {name: ["accounting" , "administration"]} )
Run Code Online (Sandbox Code Playgroud)

但不是找到配置文件id 1- 它得到了我[ 1, 2 ]因为id为2的配置文件"accounting" skills并且它"IN" operation正在数据库中执行

注意:我正在使用postgresql和问题不仅仅是描述的配置文件的特定ID(我仅用作示例) - 最初的问题是获取包含这两个提到的技能的所有配置文件.

我的activerecord join在postgres中触发以下查询

SELECT FROM "profiles" LEFT OUTER JOIN "skills" ON "skills"."profile_id" = "profiles"."id" WHERE "skills"."name" IN ('Accounting', 'Administration')
Run Code Online (Sandbox Code Playgroud)

在下面的Vijay Agrawal的回答是我已经在我的应用程序中已经有的东西,他和我的,查询使用IN通配符导致包含任何技能的配置文件ID,而我的问题是获得包含两种技能的配置文件ID.我确信必须有一种方法可以用原始问题中列出的相同查询方式来解决这个问题,我很想知道这种方式.我希望我能得到更多帮助 - 谢谢

为清楚起见,我想查询具有与概要模型的has_many关系的模型中具有多种技能的所有概要 - 使用Profileas primary表而不是skills

使用Profile作为主表的原因是在分页中我不想从相关表中获取所有技能,比如20_000或更多行,然后根据profile.state列进行过滤.相反,任何人都希望只选择5 records哪些符合profile.state , profile.user.is_active and other columns condition并匹配技能而不检索数千个无关的记录,然后再次过滤它们.

Vij*_*wal 7

你应该这样做,以获得所有profile_id具有会计和管理技能的s:

Skill.where(name: ["accounting", "administration"]).group(:profile_id).having("count('id') = 2").pluck(:profile_id)
Run Code Online (Sandbox Code Playgroud)

如果需要配置文件详细信息,可以将此查询放在Profilefor的where子句中id.

请注意2查询中的数字,它是在where子句中使用的数组的长度.在这种情况下["accounting", "administration"].length

更新::

根据更新的问题描述,pluck您可以使用select和添加子查询,以确保它在一个查询中发生.

Profile.where(id: Skill.where(name: ["accounting", "administration"]).group(:profile_id).having("count('id') = 2").select(:profile_id))
Run Code Online (Sandbox Code Playgroud)

更多的是你可以控制排序,分页和其他where子句.不要在编辑中提到有任何问题.

更新2 ::

另一种使配置文件与两种技能相交的方法(可能效率低于上述解决方案):

profiles = Profile

["accounting", "administration"].each do |name|
  profiles = profiles.where(id: Skill.where(name: name).select(:profile_id))
end
Run Code Online (Sandbox Code Playgroud)

  • @ImranNaqvi @VijayAgrawal的更新答案有效,这个表达式为"Profile.where(id:Skill.where(name:["accounting","administration"]).group(:profile_id).having("count('id') )= 2").select(:profile_id))`生成此查询`SELECT"profiles".*FROM"profiles"WHERE"profiles"."id"IN(SELECT"技能"."profile_id"FROM"技能"WHERE "技能"."名称"IN('会计','管理')GROUP BY"技能"."profile_id"HAVING(count('id')= 2))` (2认同)