SQLAlchemy中使用IN操作时如何仅取带聚合子查询的ID列
报错本质
IN()语法强制要求括号内的子查询仅返回单列结果,你编写的子查询同时选取了Model.id和func.max(Model.date)两个字段,列数不匹配直接触发了SQLite的列数校验报错。
另外你当前的子查询本身存在逻辑隐患:GROUP BY Model.relationship_id的分组规则下,直接选取非聚合的Model.id属于非标准SQL写法,在开启严格模式的数据库中会直接报错——因为同一分组内的id值不是唯一的,数据库无法确定你要返回哪条记录的id。
正确实现方案
方案1:使用元组匹配关联过滤(最符合你的业务逻辑)
无需刻意舍弃date字段,将分组结果和主表的两个关联字段做等值匹配,直接拿到每个分组最新日期对应的记录:# 构造分组子查询,仅返回分组键和最新日期 latest_group_subq = ( session.query( Model.relationship_id.label("group_rid"), func.max(Model.date).label("group_latest_date") ) .group_by(Model.relationship_id) .subquery() ) # 用元组等值过滤,不需要硬拆子查询的列 query = session.query(Model.id).filter( tuple_(Model.relationship_id, Model.date) == tuple_( latest_group_subq.c.group_rid, latest_group_subq.c.group_latest_date ) )该写法完全符合SQL标准,不会出现非聚合列取数的逻辑偏差,拿到的结果就是每个关联关系下最新日期对应的记录id。
方案2:嵌套构造单列id的子查询(适配IN()语法要求)
如果你必须使用Model.id.in_(子查询)的写法,不要直接把两列的分组结果塞到IN括号里,要在分组逻辑外层嵌套一层关联查询,让最终的子查询只返回单列id:# 先构造纯分组统计的内层子查询 group_subq = ( session.query( Model.relationship_id.label("rid"), func.max(Model.date).label("max_dt") ) .group_by(Model.relationship_id) .subquery() ) # 外层关联主表,仅选取id字段作为IN的子查询 id_subquery = ( session.query(Model.id) .join( group_subq, and_( Model.relationship_id == group_subq.c.rid, Model.date == group_subq.c.max_dt ) ) ) # 此时子查询仅返回单列id,完全满足IN()的语法要求 query = session.query(Model.id).filter(Model.id.in_(id_subquery))关于你当前临时绕过方案的说明
你现在用func.max(Model.id)的写法存在逻辑漏洞:取到的最大id并不一定是日期最新的记录。只有当Model.id是严格自增、且数据写入顺序和date字段值完全正相关时,结果才会碰巧正确,一旦存在历史数据补录、id和date值不匹配的场景,就会返回错误的结果。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

