You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()便捷方法的行为规则决定:

  1. 单个初始列的结果扁平化
    当查询语句的初始选择项为单个列时,session.exec().all()会自动扁平化结果,仅返回该列的数值列表。即使后续通过add_columns()追加了其他字段,exec()仍会优先识别初始的单个列查询模式,忽略后续追加的列,因此select_groupby_one_col()只返回了userId的字符串列表。

  2. 多初始列的结果封装
    当查询语句的初始选择项为多个列时,session.exec().all()会将每一行结果封装为元组,包含所有选中的字段(包括后续通过add_columns()追加的聚合字段),因此select_groupby_more_col()返回了包含三个元素的元组列表。

  3. 补充:正确获取多字段结果的写法
    若要让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 13:47:48