我有一个用于管理志愿者的Rails应用程序,我正在寻找编写复杂SQL查询以过滤所有志愿者记录的“Rails方式”。我有以下型号:
class Volunteer < ActiveRecord::Base
has_many :volunteer_skills
has_many :skills, through: :volunteer_skills
has_many :volunteer_lists
has_many :lists, through: :volunteer_lists
end
class VolunteerSkill < ActiveRecord::Base
belongs_to :volunteer
belongs_to :skill
end
class VolunteerList < ActiveRecord::Base
belongs_to :volunteer
belongs_to :list
end
如果@lists
是List
个ID的数组,而@skills
是Skill
个ID的数组
@lists
,而不是@skills
。@lists
,而不是@skills
。{/ li>
@lists
任意@skills
找到所有志愿者。@lists
的所有@skills
上找到所有志愿者。看到此StackOverflow question后,我使用.find_by_sql
创建了此问题的解决方案。当查询涉及查找包含ALL @skills
的志愿者时,我构建了INTERSECT
查询。当查询涉及找到任何@skills
的志愿者时,我构建了一个UNION
查询。如果查询涉及查找没有@skills
的志愿者,我会使用EXCEPT
格式化查询。
不幸的是,这个解决方案并不像使用ActiveRecord那样友好。我对编程很陌生,但我试图解决的问题似乎相当简单/普遍。是否有更好的解决方案可以使用ActiveRecord?
我非常感谢任何想法!
答案 0 :(得分:2)
您可以使用ActiveRecord完成大部分工作,但仍需要使用一些SQL片段。
首先,为所有@lists
上的所有志愿者制作一个范围:
class Volunteer < ActiveRecord::Base
scope :on_any_list -> lists { joins(:volunteer_list).where(volunteer_id: lists) }
end
然后为每个技能标准添加条件。
任何@skills
的志愿者都很容易:
on_any_list(@lists).joins(:volunteer_skills).where(skill_id: @skills).distinct
对于所有@skills
的志愿者,您需要构建一个查询:
query = on_any_list(@lists)
@skills.each do |skill|
query = query.where("exists (select 1 from volunteer_skills vs " +
"where vs.volunteer_id = volunteers.id and skill_id = ?)", skill)
end
不幸的是,ActiveRecord的not
不允许您构建上述任何一个的相反查询,因此您需要从头开始执行缺少技能的查询。
没有全部@skills
的志愿者:
on_any_list(@lists).
where("not exists (select 1 from volunteer_skills vs " +
"where vs.volunteer_id = volunteers.id and skill_id in (?))", @skills)
最后,最狡猾的志愿者没有任何@skills
:
on_any_list(@lists).
joins("left join volunteer_skills vs on volunteers.id = vs.volunteer_id").
where(skill_id: @skills)
where("volunteer_skills.id is null").
distinct
(有an AREL way to do the outer join,但我认为这不容易。)
编写和理解这些技术所需的各种技术让我想知道,作为一个整体,它们是否真的比SQL版本更好。至少on_any_lists
部分效果很好。