我有这样的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
子句以及如何克服它?
答案 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