如何强制 SQLAlchemy ORM 在子查询中包含 table?

How do I force SQLAlchemy ORM to include table in subquery?

我有两个 tables 用户和访客,内容如下:

用户:

+----+-------------+
| id |    email    |
+----+-------------+
|  1 | a.b@foo.com |
|  2 | b.c@bar.com |
+----+-------------+

访客:

+----+---------+------+
| id | user_id | addr |
+----+---------+------+
|  1 |  NULL   |  1   |
|  2 |  NULL   |  2   |
|  3 |  NULL   |  1   |
|  4 |  NULL   |  2   |
|  5 |  NULL   |  3   |
|  6 |    1    |  4   |
|  7 |  NULL   |  5   |
|  8 |  NULL   |  6   |
|  9 |    2    |  2   |
+----+---------+------+

我想获取 table 访问者 的所有 ID,这些 ID 不包含在地址列表中,这些地址链接到存储在 table 用户.

例如:

SELECT visitors.id
FROM visitors
WHERE visitors.addr NOT IN (
    SELECT DISTINCT visitors.addr
    FROM user, visitors
    WHERE user.email LIKE '%bar%' AND visitors.user_id = user.id
)

内部 select 语句将 return addr 2 因此外部语句将 return 6 行的访问者 ID '有 add == 2(即除了 2、4 和 9 之外的所有)。 我试着用这个 SQLalchemy 语句来做到这一点:

subquery = (
  session.query(Visitors.addr).distinct()
  .filter(User.email.like('%bar%'), Visitors.user_id == User.id)
  .subquery()
)

mainquery = session.query(Visitors.id).filter(Visitors.addr.notin_(subquery))

这是由 SQLalchemys ORM 创建的 SQL 语句:

SELECT visitors.id AS visitors_id
FROM visitors
WHERE visitors.addr NOT IN (SELECT DISTINCT visitors.addr
FROM user
WHERE user.email LIKE ? AND visitors.user_id = user.id)

关键区别在于子查询不再包含 FROM 子句中的访问者,缺少 table 意味着 visitor.id 2 和 4 是 returned,即使它们已分配地址 2。有没有办法强制 SQLAlchemy 在 FROM 子句中包含给定的 table,或者我应该尝试使用 JOIN 来获得相同的结果?

编辑:使用的 SQLAlchemy 版本是 1.3.20。从那时起,我将其更新为 1.4.23,以便能够按照@ian-wilson 的建议使用 scalar_subquery。 使用的数据库分别是 SQLite 3.35.5 和 MySQL 5.7.34。 使用 scalar_subquery 函数没有解决我的问题,对子查询使用连接可以。

我会像你提到的那样在这里使用连接,但你也应该考虑尝试 scalar_subquery (1.4) 或 as_scalar (<1.4) 来生成 in_ 子句。

    subquery = session.query(
        Visitors.addr.distinct()
    ).join(
        User,
        Visitors.user_id == User.id
    ).filter(
        User.email.like('%bar%')
    ).scalar_subquery()

    mainquery = session.query(Visitors.id).filter(Visitors.addr.notin_(subquery))

https://docs.sqlalchemy.org/en/14/orm/query.html#sqlalchemy.orm.Query.scalar_subquery