SQLModel中单列分组查询为何返回字符串列表而非字典列表?
SQLModel分组查询返回结果差异的原因分析
问题场景
使用SQLModel创建数据模型并执行分组查询时,两个函数返回结果格式存在明显差异:
select_groupby_one_col()预期返回包含userId和compl键的字典列表,但实际仅返回userId的字符串列表;select_groupby_more_col()返回包含userId、year、compl的元组列表。
示例代码
from sqlmodel import SQLModel, create_engine, Field, Session, select, func import random from pandas import date_range class Model(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) year: int month: int userId: str = Field(max_length=10) compl: float = Field(...) sqlite_file_name = "database.db" sqlite_url = f"sqlite:///{sqlite_file_name}" engine = create_engine('sqlite:///:memory:', echo=True) def create_db_and_tables(): SQLModel.metadata.create_all(engine) def create_records(): date_array = date_range( start = '2017-01-01', periods = 50, freq='MS' ).date list_records = [] for i,d in enumerate(date_array): prod_dict = Model( year = d.year, month = d.month, userId=f'W00000000{random.randint(1,5)}', compl = i, ) list_records.append(prod_dict) with Session(engine) as session: session.bulk_save_objects(list_records) session.commit() def select_groupby_one_col(): with Session(engine) as session: statement = select( Model.userId, ).group_by( Model.userId ) statement = statement.add_columns( func.sum(Model.compl) ) results = session.exec(statement).all() return results def select_groupby_more_col(): with Session(engine) as session: statement = select( Model.userId, Model.year ).group_by( Model.userId ) statement = statement.add_columns( func.sum(Model.compl) ) results = session.exec(statement).all() return results def main(): create_db_and_tables() create_records() one_col_results = select_groupby_one_col() more_col_results = select_groupby_more_col() print(one_col_results) print(more_col_results) if __name__ == "__main__": main()
打印结果
['W000000001', 'W000000002', 'W000000003', 'W000000004', 'W000000005'] [('W000000001', 2017, 109.0), ('W000000002', 2017, 386.0), ('W000000003', 2018, 332.0), ('W000000004', 2017, 148.0), ('W000000005', 2017, 250.0)]
差异原因
这一差异由SQLModel的session.exec()便捷方法的行为规则决定:
单个初始列的结果扁平化
当查询语句的初始选择项为单个列时,session.exec().all()会自动扁平化结果,仅返回该列的数值列表。即使后续通过add_columns()追加了其他字段,exec()仍会优先识别初始的单个列查询模式,忽略后续追加的列,因此select_groupby_one_col()只返回了userId的字符串列表。多初始列的结果封装
当查询语句的初始选择项为多个列时,session.exec().all()会将每一行结果封装为元组,包含所有选中的字段(包括后续通过add_columns()追加的聚合字段),因此select_groupby_more_col()返回了包含三个元素的元组列表。补充:正确获取多字段结果的写法
若要让select_groupby_one_col()返回包含userId和聚合结果的元组(或字典),应直接在select()中指定所有需要的字段,而非用add_columns()追加:def select_groupby_one_col(): with Session(engine) as session: statement = select( Model.userId, func.sum(Model.compl).label("compl") # 用label指定别名方便后续转换 ).group_by(Model.userId) results = session.exec(statement).all() # 转换为字典列表 # results = [{"userId": user_id, "compl": total_compl} for user_id, total_compl in results] return results
另外需注意:select_groupby_more_col()的查询不符合SQL标准——year字段既不在GROUP BY子句中,也未被聚合函数包裹,在严格模式的数据库中会报错,但SQLite默认允许这种非标准分组,返回的year值是分组内任意一条记录的年份。
内容的提问来源于stack exchange,提问作者Santiago Cuervo
相关产品推荐
相关产品推荐

