首页 > 解决方案 > SQLAlchemy 未在正确选择时构建 JOIN

问题描述

我正在尝试将以下查询转换为 SQLAlchemy ORM 查询:

SELECT applications.*, 
       appversions.version 
FROM   applications 
       JOIN appversions 
         ON appversions.id = (SELECT id 
                              FROM   appversions 
                              WHERE  appversions.app_id = applications.id 
                              ORDER  BY sort_ver DESC 
                              LIMIT  1) 

表的模型如下:

Base = declarative_base()

class Application(Base):
    __tablename__ = 'applications'

    id       = Column(Integer, primary_key = True)
    group    = Column(Unicode(128))
    artifact = Column(Unicode(128))

    versions = relationship("AppVersion", backref = "application")

class AppVersion(Base):
    __tablename__ = 'versions'

    id        = Column(Integer, primary_key = True)
    app_id    = Column(Integer, ForeignKey('applications.id'))
    version   = Column(Unicode(64))
    sort_ver  = Column(Unicode(64))

到目前为止,我提出的查询是:

subquery = select([AppVersion.id]). \
                where(AppVersion.app_id == Application.id). \
                order_by(AppVersion.sort_ver). \
                limit(1). \
                alias()


query = session.query(Application). \
                join(AppVersion, AppVersion.id == subquery.c.id) \
                .all()

但是,这会产生以下 SQL 语句和错误:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: anon_1.id
[SQL: SELECT applications.id AS applications_id, applications."group" AS applications_group, applications.artifact AS applications_artifact
FROM applications JOIN versions ON versions.id = anon_1.id]

我尝试了各种不同的方法来生成子查询并尝试“附加”子 SELECT 命令,但没有任何积极影响。

有没有办法强制 SQLAlchemy 查询生成器正确附加子 SELECT?

标签: pythonsqlalchemy

解决方案


感谢@Ilja Everilä 在正确方向上的推动,生成正确查询的代码是:

subquery = select([AppVersion.id]). \
                where(AppVersion.app_id == Application.id). \
                order_by(AppVersion.sort_ver). \
                limit(1). \
                correlate(Application)

query = session.query(Application). \
                join(AppVersion, AppVersion.id == subquery) \
                .all()

主要的变化是使用了correlate()方法,它改变了 SQLAlchemy 构造子查询的方式。

要解释为什么会这样,需要对 SQL 子查询的分类和处理方式有所了解。我发现的最佳解释来自 https://www.geeksforgeeks.org/sql-correlated-subqueries/

对于普通的嵌套子查询,内部 SELECT 查询首先运行并执行一次,返回主查询使用的值。但是,相关子查询对外部查询考虑的每个候选行执行一次。换句话说,内部查询由外部查询驱动。


推荐阅读