首页 > 解决方案 > Sqlalchemy 使用 ~ 和 and_, or_

问题描述

我有一个表达:

recent_oa = db_session.query(exists().where(
    and_(
        and_(Jobs.candidate_id == candidate_id, Jobs.interview_type == 'EVALUATION', Jobs.disposition_date < datetime.now()-timedelta(days=1)),
        ## I want this to evaluate to be True
        and_(~Jobs.candidate_id == candidate_id, Jobs.interview_type == 'IN_HOUSE', Jobs.disposition_date > '2017-01-01'),
        ## and I want this to evaluate to False    
    )
)).scalar()

recent_oa
>> True

所以这recent_oa将评估为True

我想创建多个条件,如果第一行评估为True,然后其余行评估为Falserecent_oa则评估为True

我该怎么写这个表达式?我的方式似乎不起作用。

标签: pythonsqlalchemy

解决方案


您需要在需要反转的表达式前面加上~, 所以~ and_(...).

请注意,所有布尔逻辑运算符都有运算符和函数。~也可用作not_()函数,类似地,and_()可以用 表示&,并且or_()可以用 表示|。我会坚持一种风格或另一种风格,而不是两者兼而有之。

所以你的要求可以表示为

and_(
    and_(
        Jobs.candidate_id == candidate_id,
        Jobs.interview_type == 'EVALUATION',
        Jobs.disposition_date < datetime.now()-timedelta(days=1)
    ),

    not_(
        and_(
            not_(Jobs.candidate_id == candidate_id),
            Jobs.interview_type == 'IN_HOUSE',
            Jobs.disposition_date > '2017-01-01'
        )
    )
)

或作为

(
    (
        (Jobs.candidate_id == candidate_id)
        & (Jobs.interview_type == 'EVALUATION')
        & (Jobs.disposition_date < datetime.now()-timedelta(days=1))
    )

    & (
        ~ (
            ~ (Jobs.candidate_id == candidate_id)
            & (Jobs.interview_type == 'IN_HOUSE')
            & (Jobs.disposition_date > '2017-01-01')
        )
    )
)

因为~, &and|运算符的优先级低于比较运算符,所以您必须(...)在上面的每个测试周围使用括号,以确保运算符适用于整个column == valuecolumn < value测试。

我不是 100% 确定你打算使用not_(Jobs.candidate_id == candidate_id),但如果你这样做了,那么我就在!=那里使用,所以Jobs.candidate_id != candidate_id并放弃该not_()测试的条件。

接下来,查询可能无论如何都不会给出正确的结果。它会告诉您是否有 1 行或更多行符合您的条件,即候选人 ID 匹配的行,以及面试类型和处置日期的特定条件。它不会告诉你是否只有这样的行匹配。所以如果有这样的行:

 candidate_id  |  disposition_date |  interview_type
---------------|-------------------|-----------------
 42            | 2018-01-01        | EVALUATION
 42            | 2018-01-02        | IN_HOUSE

那么你会得到True,因为有 1 行符合你的条件。该IN_HOUSE行被过滤掉。您不会到达False这里,因为过滤器中存在另一行并不重要。

在这种情况下,您想在单独的子句上使用NOT表达式布尔逻辑,而不是条件,因此第二行的存在会导致结果与另一行相反:EXISTSFalseTrue

SELECT id
FROM jobs
WHERE
    EXISTS(candidate_id = ? AND interview_type = ? AND disposition_date < ?)
    AND NOT EXISTS(candidate_id = ? AND interview_type = ? AND disposition_date > ?)

所以创建两个单独的存在查询

candidate = db_session.query(Jobs).filter(Jobs.candidate_id == candidate_id)
has_evaluation = (
    candidate
    .filter(Jobs.interview_type == 'EVALUATION')
    .filter(Jobs.disposition_date < datetime.now() - timedelta(days=1))
)
has_inhouse = (
    candidate
    .filter(Jobs.interview_type == 'IN_HOUSE')
    .filter(Jobs.disposition_date > '2017-01-01')
)
recent_oa = (
    db_session.query(Jobs.id)
    .filter(has_evaluation.exists())
    .filter(not_(has_inhouse.exists())) # or .filter(~has_inhouse.exists())
    .scalar()
)

这使用产生分离条件的Query.exists()方法。EXISTS


推荐阅读