我正在尝试查询拥有所有请求技能的人员。不确定动态构建查询的最佳方法是什么。
ORM对象如下。人与技能之间的多对多,连接表不包括在这里:
Person
name=John, skills=[Skill(id=1)] # John knows Python
name=Eve, skills=[Skill(id=1), Skill(id=2)] # Eve knows Python and SQLAlchemy
Skill
id=1, name=Python
id=2, name=SQLAlchemy
id=3, name=Social skills
在搜索页面上,用户选择一项或多项技能,并根据此创建人员的搜索结果。让我们说用户想要找到一个知道Python和SQLAlchemy的人。应用程序从表单提交中获取两个ID的列表,[1,2]。我得到了以下有关工作的查询,找到具有匹配技能的人:
session.query(Person)./
join(Skill, Person.skills).filter(Skill.id.in_(list_of_skill_ids))
但是,使用in_子句会导致找到John人,因为他具有所需技能之一。但搜索结果应仅显示具有所有所需技能的人员。基本上我想要的是像in_(),但是用户和而不是或来匹配id列表。我可能应该用这个替换in_()部分,它不直接获取id列表:
filter(Person.id == 1, Person.id == 2)
但是,使用从表单收到的技能ID列表以编程方式构建查询的最简单方法是什么? id的数量可以是1..n。或者这是否是通过其子女查询父母的正确方法?
答案 0 :(得分:5)
最直接的SA解决方案是为每项技能使用any
:
list_of_skill_ids = [1, 2]
qry = session.query(Person)
for skill_id in list_of_skill_ids:
qry = qry.filter(Person.skills.any(Skill.id == skill_id))
print('\n'.join("{}".format(_) for _ in qry.all()))
这可能不是大数据集中性能最高的查询,但它非常干净。
另外,下面也应该有效。 (类似于RedBaron的答案,但将检查直接放在查询中):
qry = (session.query(Person)
.join(Skill, Person.skills)
.filter(Skill.id.in_(list_of_skill_ids))
.group_by(Person)
.having(len(list_of_skill_ids) <= func.count(Skill.id))
)
print('\n'.join("{}".format(_) for _ in qry.all()))
答案 1 :(得分:1)
一种方法可以是找到满足每个技能组的人员列表,然后获取所有列表的交集。但在这种情况下的缺点是,如果你必须搜索10个技能组,你需要发出10个查询。
更好的方法是按人员ID对原始查询进行分组,然后计算每个人的技能数量。只保留技能数量与您的技能组合相匹配的人员。也许是这样的?
skill_count = session.query(Person.id.label('id'),func.count(Skill.id.distinct()).label('scount')).\
select_from(Person).\
join(Person.skills).\
filter(Skill.id.in_(list_of_skill_ids)).\
group_by(Person.id).all()
matching_pid_list = [x.id for x in skill_count if x.scount == len(list_of_skill_ids)]