Flask+SQLAlchemy Core构建查询时避免FROM子句重复追加别名表的方法
你遇到的问题是SQLAlchemy在生成SQL时重复添加了已经通过outerjoin关联的INFRA_RES表别名,导致FROM子句里出现了额外的表项。这是因为当你直接用select([SYSTEM])指定查询列,再结合select_from(STMT)时,SQLAlchemy没能正确识别WHERE条件中的别名属于已有的关联结构,反而把它当成了一个新的表引用,从而重复添加到FROM中。
下面是两种可行的解决方案:
方案1:用with_only_columns限定查询列
先构建完整的关联查询结构,再通过with_only_columns只保留SYSTEM的列,最后添加过滤条件。这种方式能让SQLAlchemy明确WHERE条件中的别名属于已关联的表:
SYSTEM = self.__TABLES.SYSTEM RESPONSIBLE = self.__TABLES.responsible INFRA_RESPONSIBLE = RESPONSIBLE.alias('INFRA_RES') SYSTEM_RESPONSIBLE = RESPONSIBLE.alias('SYS_RES') # 先构建包含所有关联的基础查询 base_stmt = SYSTEM \ .outerjoin(INFRA_RESPONSIBLE, INFRA_RESPONSIBLE.c.id == SYSTEM.c.infrares) \ .outerjoin(SYSTEM_RESPONSIBLE, SYSTEM_RESPONSIBLE.c.id == SYSTEM.c.sysres) # 基于基础查询,只选择SYSTEM的列并添加过滤条件 final_stmt = select(base_stmt).with_only_columns([SYSTEM.c]).where(INFRA_RESPONSIBLE.c.id == 1)
方案2:直接从关联结构中选择目标列
明确将关联后的Join对象作为select_from的数据源,同时指定要查询的SYSTEM列,这样SQLAlchemy会将整个关联结构视为单一的FROM源,不会重复添加表:
SYSTEM = self.__TABLES.SYSTEM RESPONSIBLE = self.__TABLES.responsible INFRA_RESPONSIBLE = RESPONSIBLE.alias('INFRA_RES') SYSTEM_RESPONSIBLE = RESPONSIBLE.alias('SYS_RES') # 构建关联后的Join对象 join_clause = SYSTEM \ .outerjoin(INFRA_RESPONSIBLE, INFRA_RESPONSIBLE.c.id == SYSTEM.c.infrares) \ .outerjoin(SYSTEM_RESPONSIBLE, SYSTEM_RESPONSIBLE.c.id == SYSTEM.c.sysres) # 选择SYSTEM的列,并从关联结构获取FROM源 final_stmt = select([SYSTEM.c]).select_from(join_clause).where(INFRA_RESPONSIBLE.c.id == 1)
最终生成的SQL
两种方案都会生成你期望的SQL语句:
SELECT "it".systems.* FROM "it".systems LEFT OUTER JOIN "it".responsible as "INFRA_RES" ON "INFRA_RES".id = "it".systems.infrares LEFT OUTER JOIN "it".responsible as "SYS_RES" ON "SYS_RES".id = "it".systems.sysres WHERE "INFRA_RES".id = 1
本质上都是让SQLAlchemy明确WHERE条件中的别名属于已存在的关联结构,避免误判为新表引用。
内容的提问来源于stack exchange,提问作者Drachlowski

