Как отмечается в комментариях, вам потребуется операция FORALL
( универсальный квантификатор ) или реляционное деление .
FORALL x ( p(x) )
может быть выражено как
NOT ( EXISTS x ( NOT ( p(x) ) ) )
, что немного громоздко и трудно для размышления, если вы не знаете о FORALL
и их отношениях.С учетом ваших моделей это может выглядеть следующим образом:
def get_skilled_candidates(skill_ids):
# Form a temporary derived table using unions
skills = db.union_all(*[
db.select([db.literal(sid).label('skill_id')])
for sid in skill_ids]).alias()
return Candidate.query.\
filter(
~db.exists().select_from(skills).where(
~db.exists().
where(db.and_(skill_candidate.c.skill_id == skills.c.skill_id,
skill_candidate.c.candidate_id == Candidate.id)).
correlate_except(skill_candidate))).\
all()
Конечно, есть и другие способы выразить тот же запрос, например:
def get_skilled_candidates(skill_ids):
return Candidate.query.\
join(skill_candidate).\
filter(skill_candidate.c.skill_id.in_(skill_ids)).\
group_by(Candidate.id).\
having(db.func.count(skill_candidate.c.skill_id.distinct()) ==
len(set(skill_ids))).\
all()
, который по сути проверяет счетчиком, что все идентификаторы уменийбыли сопоставлены.
При использовании Postgresql вы также можете сделать:
from sqlalchemy.dialects.postgresql import array_agg
def get_skilled_candidates(skill_ids):
# The double filtering may seem redundant, but the WHERE ... IN allows
# the query to use indexes, while the HAVING ... @> does the final filtering.
return Candidate.query.\
join(skill_candidate).\
filter(skill_candidate.c.skill_id.in_(skill_ids)).\
group_by(Candidate.id).\
having(array_agg(skill_candidate.c.skill_id).contains(skill_ids)).\
all()
Это несколько эквивалентно частично Python-решению из другого ответа.
Кроме того, агрегатEVERY
можно использовать:
def get_skilled_candidates(skill_ids):
# Form a temporary derived table using unions
skills = db.union_all(*[
db.select([db.literal(sid).label('skill_id')])
for sid in skill_ids]).alias()
# Perform a CROSS JOIN between candidate and skills
return Candidate.query.\
join(skills, db.true()).\
group_by(Candidate.id).\
having(db.func.every(
db.exists().
where(db.and_(skill_candidate.c.skill_id == skills.c.skill_id,
skill_candidate.c.candidate_id == Candidate.id)).
correlate_except(skill_candidate))).\
all()