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

为何SQLAlchemy调用PostgreSQL函数后fetchall结果格式异常?

问题:SQLAlchemy调用PostgreSQL表函数返回结果格式异常

问题现象

使用SQLAlchemy调用PostgreSQL的getTopSales函数时,db_result.fetchall()返回的结果是每个元素为包含字符串化元组的单元素元组的列表:

[('("Murder in the First (1995)",2018,57)',), ('("U-571 (2000)",2020,53)',), ('("Money Train (1995)",2019,52)',), ('("Picnic at Hanging Rock (1975)",2019,52)',)]

而预期结果应为多列组成的元组列表:

[("Murder in the First (1995)",2018,57),("U-571 (2000)",2020,53),("Money Train (1995)",2019,52), ("Picnic at Hanging Rock (1975)",2019,52)]

现有代码

Python调用代码

db_conn = None
db_conn = db_engine.connect()

db_result = db_conn.execute(select([func.getTopSales(2018,2020)]))

print(db_result.fetchall())        
return db_result

PostgreSQL函数定义

create or replace function getTopSales(y1 int, y2 int) 
returns table (titulo char,
               año int,
               ventas int) as $$
    with prod_year_sales as ( /*The products with their sales and the year of the sale*/
    select
        orderdetail.prodid,
        extract (year from orders.orderdate) as "year",
        count(orderdetail.prodid) as sales
    from
        orderdetail
    natural join 
        orders
    where
        extract(year from orders.orderdate) between y1 and y2 group by orderdetail.prodid, "year"),
    max_sales_year_prod as ( /*The products with most sales by year*/
    select
        a.prodid,
        a."year",
        a.sales
    from
        prod_year_sales a
    inner join (
        select
            "year",
            max(sales) sales
        from
            prod_year_sales
        group by
            ("year")) as b on
        a."year" = b."year"
        and a.sales = b.sales)
    select /*Get title of the movies that correspond to the sold products*/
        movietitle,
        main."year",
        sales
    from
        imdb_movies im
    inner join (
        select
            movieid,
            "year",
            sales
        from
            max_sales_year_prod m
        inner join products p on
            m.prodid = p.prodid) as main on im.movieid  = main.movieid order by(sales) desc;
$$ language sql;

解决方法

问题根源

原代码中select([func.getTopSales(2018,2020)])将表函数的返回结果当作单个列处理,PostgreSQL会自动把整行数据序列化为字符串形式的元组,导致返回结果格式异常。正确的做法是将表函数当作一张表来查询。

修正后的代码

提供两种可行的修正方案:

方案1:执行原生SQL语句

直接用SELECT * FROM getTopSales(...)的方式调用表函数,SQLAlchemy会识别出多列结果:

from sqlalchemy import text

db_conn = db_engine.connect()
# 带参数的原生SQL调用,避免SQL注入
db_result = db_conn.execute(text("SELECT * FROM getTopSales(:y1, :y2)"), {"y1": 2018, "y2": 2020})
print(db_result.fetchall())
return db_result

方案2:使用SQLAlchemy的Table-Valued函数语法

通过table_valued()方法明确指定表函数返回的列名,让SQLAlchemy正确解析结果:

from sqlalchemy import select, func

db_conn = db_engine.connect()
# 声明表函数的返回列
top_sales_func = func.getTopSales(2018, 2020).table_valued("titulo", "año", "ventas")
db_result = db_conn.execute(select(top_sales_func))
print(db_result.fetchall())
return db_result

额外说明

修正后,若需要将结果转为字典列表,可使用db_result.mappings().fetchall(),会得到每个元素为字典的列表,键对应函数定义的列名。

内容的提问来源于stack exchange,提问作者Diego Gonzalez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 06:45:32