在sqlalchemy中,如何获得一个匹配表中所有行的查询,以便另一个表中没有行链接到它?

时间:2012-09-02 17:37:14

标签: python sqlalchemy

我有两个表,“event”和“event_exception”。表“事件”有一个(布尔)列“常规”,表示事件是否每年定期发生。表“event_exception”有一列“event_id”,一列“year”和一个(boolean)列“出现”。

数据将以这种方式解释:

  • 有些事件每年都会定期发生,但有时它们却不例外,它们由“event.regular == True”的“事件”行和“event_exception.occurs =”的“event_exception”行编码错误的“每年特别省略事件。
  • 有些事件并不经常发生,但有时他们会特别注意。这是由“event.regular == False”的“事件”行和事件异常发生的每年“event_exception.occurs == True”的“event_exception”行编码。

如何编写一个匹配今年将发生的所有事件的查询?

我的猜测就像是

session.query(Event, EventException).filter(Event.id==EventException.event_id)
.filter(EventException.year==current_year).
filter(or_(
    and_(Event.regular==1, EventException.occurs==0, having(count(EventException)==0)),
    and_(Event.regular==0, EventException==1, having(count(EventException)>0)
))

,但我不确定having是否可以在and_中使用。

2 个答案:

答案 0 :(得分:1)

在答案上不确定,我只想注意一些事情,因为我最近在sqlalchemy下遇到布尔查询问题......

  • 您应该将布尔比较作为'== True'和'== False'。 MySQL将Boolean存储为1/0,但PostgreSQL和其他存储为true / false,Python也是如此。 SqlAlchemy会根据需要进行转换,但是当你查看其他人的代码时...那看起来像是INT比较,而不是BOOL。对于将来不得不关注这个问题的其他人来说,这将更容易。

  • 根据SQL存储引擎和列默认值,您可能无法获得所需的结果。如果集合中允许NULL值,则您的比较将不匹配。您可以通过以下搜索获得所需的结果:

    Event.regular.op('IS NOT')(True)

    Event.regular.op('IS')(False)

    sqlalchemy.sql.functions.coalesce( Event.regular , False ) != True sqlalchemy.sql.functions.coalesce( Event.regular , False ) == False

在代码的第一位,我们搜索非True的项 - 它们都是False和NULL。 regular != True的结果集仅包含False项; regular IS NOT True的结果集包含FalseNull

在第二位代码中,数据库会在比较之前将Null值合并到False

你可能不需要进行这些比较,但是如果你做的机会和你的结果看起来不正确,这可能就是原因。

答案 1 :(得分:1)

如果没有HAVING,则无法使用GROUP BY。无论如何,在这种情况下,它们都不是必需的,所需要的是EXISTS。假设您已经为Event.exceptions定义了SQLAlchemy关系,则以下表达式应该起作用:

session.query(Event).filter(or_(
    and_(
        Event.regular == True,
        ~Event.exceptions.any(and_(
            EventException.year == current_year,
            EventException.occurs == False,
        )),
    ),
    and_(
        Event.regular == False,
        Event.exceptions.any(and_(
            EventException.year == current_year,
            EventException.occurs == True,
        )),
    ),
))

并生成如下SQL:

SELECT event.*
FROM event
WHERE
    (
        event.regular = true
        AND NOT EXISTS (
            SELECT 1
            FROM event_exception
            WHERE
                event.id = event_exception.event_id
                AND event_exception.year == :year
                AND event_exception.occurs = false
        )
    )
    OR
    (
        event.regular = false
        AND EXISTS (
            SELECT 1
            FROM event_exception
            WHERE
                event.id = event_exception.event_id
                AND event_exception.year == :year
                AND event_exception.occurs = true
        )
    )

编辑:第一个条件应该使用NOT EXISTS而不是