Sqlalchemy from_self在引用外部别名表

时间:2018-05-23 05:34:52

标签: python sqlalchemy

我有这样的sqlalchemy查询:

E = aliased(Entity, name='e')
db.session.query(E) \
    .filter(E.user_id == user_id) \
    .filter(
        db.session.query(Entity) \
            .distinct(Entity.original_id) \
            .order_by(Entity.original_id, Entity.id.desc())
            .filter(Entity.ref_id == E.ref_id) \
            .from_self(Entity.flag) \
            .order_by(Entity.timestamp.desc()) \
            .limit(1).as_scalar()
    ) \
    .order_by(E.timestamp) \
    .all()

它(大致)生成以下SQL:

SELECT *
FROM entity AS e
WHERE e.user_id = 1 AND (
    SELECT anon_1.entity_flag AS flag
    FROM (
        SELECT DISTINCT ON (entity.original_id)
            entity.flag AS entity_flag, entity.timestamp AS entity_timestamp
        FROM entity, entity AS e # THIS "entity AS e" SHOULD NOT BE HERE
        WHERE
            entity.ref_id = e.ref_id
            ORDER BY entity.original_id, entity.id DESC
    ) AS anon_1
    ORDER BY anon_1.entity_timestamp
    LIMIT 1
) ORDER BY e.timestamp;

因为它以某种方式将entity AS e添加到内部查询FROM子句中,导致WHERE entity.ref_id = e.ref_id不应该引用外部表。

为什么这个无关的entity AS e被添加到内部FROM子句以及如何克服它?

1 个答案:

答案 0 :(得分:2)

可能您已找到Query.from_self()的限制:

  

自动别名功能仅适用于有限方式,适用于简单的过滤器和排序。更加雄心勃勃的构造(例如引用连接中的实体)应该更喜欢使用显式子查询对象,通常使用Query.subquery()方法来生成显式子查询对象。始终通过查看SQL来测试查询结构,以确保特定结构能够达到预期效果!

一般来说,SQLAlchemy"自动关联"从immediate enclosing query only考虑​​FROM对象的相关性,如果需要更深层次的嵌套,则必须使用显式关联。

另一方面,在你的情况下,这没有帮助。由于某种原因,添加对Query.correlate()的调用并没有突破Query.from_self()的边界,尽管在文档中甚至提到了它们的使用:

  

关联参数在使用Query.from_self()时或Query.subquery()返回的子查询嵌入另一个select()构造中的情况下生效。

解决方案是使用显式子查询:

In [65]: sq = session.query(Entity.flag, Entity.timestamp).\
    ...:     distinct(Entity.original_id).\
    ...:     order_by(Entity.original_id, Entity.id.desc()).\
    ...:     filter(Entity.ref_id == E.ref_id).\
    ...:     correlate(E).\
    ...:     subquery()

In [66]: q = session.query(E).\
    ...:     filter(E.user_id == 123).\
    ...:     filter(session.query(sq.c.flag).
    ...:            order_by(sq.c.timestamp.desc()).
    ...:            limit(1).
    ...:            as_scalar()).\
    ...:     order_by(E.timestamp)
    ...:            

In [67]: print(q.statement.compile(dialect=postgresql.dialect()))
SELECT e.id, e.ref_id, e.timestamp, e.original_id, e.user_id, e.flag 
FROM entity AS e 
WHERE e.user_id = %(user_id_1)s AND (SELECT anon_1.flag 
FROM (SELECT DISTINCT ON (entity.original_id) entity.flag AS flag, entity.timestamp AS timestamp 
FROM entity 
WHERE entity.ref_id = e.ref_id ORDER BY entity.original_id, entity.id DESC) AS anon_1 ORDER BY anon_1.timestamp DESC 
 LIMIT %(param_1)s) ORDER BY e.timestamp