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

如何用Python+SQLAlchemy一次查询PostgreSQL时序分区销售数据

一次查询搞定按周区间分组的销售统计

嘿,这个场景我太熟了!完全不用跑6次查询,咱们用SQLAlchemy的分组查询结合case表达式,一次就能把所有6个区间的销售数据捞出来,效率还高得多。下面给你两种方案,按需选择:

方案1:按星期几映射区间(灵活适配每周)

如果你的区间是固定的「星期X-星期Y」模式,比如周六-周日、周日-周一这类,推荐用PostgreSQL的时间函数把Unix时间戳转成星期几,再用case映射到对应的区间标签,最后分组统计。

步骤1:导入必要的SQLAlchemy工具

from sqlalchemy import func, case, and_

步骤2:构建区间标签映射

先把Unix时间戳转成PostgreSQL的时间类型,再提取星期几(注意PostgreSQL里dow字段:0=周日,1=周一,...,6=周六),然后用case把每个星期几对应到你的目标区间:

# 定义每个星期几对应的区间标签
interval_label = case(
    [(func.extract('dow', func.to_timestamp(SalesTable.when_in_unix_time)) == 6, '周六-周日'),
     (func.extract('dow', func.to_timestamp(SalesTable.when_in_unix_time)) == 0, '周日-周一'),
     (func.extract('dow', func.to_timestamp(SalesTable.when_in_unix_time)) == 1, '周一-周二'),
     (func.extract('dow', func.to_timestamp(SalesTable.when_in_unix_time)) == 2, '周二-周三'),
     (func.extract('dow', func.to_timestamp(SalesTable.when_in_unix_time)) == 3, '周三-周四'),
     (func.extract('dow', func.to_timestamp(SalesTable.when_in_unix_time)) == 4, '周四-周五')],
    else_='周五-周六'  # 可以根据你的需求调整,或者设为None忽略
)

步骤3:执行分组查询

指定整个统计周期的起止时间,然后按区间标签分组,统计销售数据(这里以统计销售笔数为例,你也可以换成func.sum(SalesTable.amount)统计金额):

# 先定义整个一周的时间范围(比如最近7天的起止Unix时间戳)
overall_start_time = ...  # 你需要的一周起始Unix时间
overall_end_time = ...    # 你需要的一周结束Unix时间

# 构建查询
query = db.query(
    interval_label.label('interval'),
    func.count(SalesTable.id).label('sales_count'),
    # 如果需要每个区间的实际起止时间,可以加下面两行
    func.min(SalesTable.when_in_unix_time).label('interval_start'),
    func.max(SalesTable.when_in_unix_time).label('interval_end')
).filter(
    SalesTable.when_in_unix_time >= overall_start_time,
    SalesTable.when_in_unix_time <= overall_end_time
).group_by('interval').order_by('interval')

# 获取结果
results = query.all()

返回的results里每个元素是一个元组,包含区间名称、销售数量、区间起止时间(如果加了的话),直接就能用来生成图表了。


方案2:按固定时间范围匹配区间(精准指定每个区间的时间)

如果你的区间是特定的时间范围(比如某一周的周六0点到周日24点这种固定值),可以直接用case匹配每个时间区间:

# 提前计算好每个区间的起止Unix时间戳
sat_sun_start = ...
sat_sun_end = ...
sun_mon_start = ...
sun_mon_end = ...
# 其余4个区间同理...

interval_label = case(
    [(and_(SalesTable.when_in_unix_time >= sat_sun_start, SalesTable.when_in_unix_time <= sat_sun_end), '周六-周日'),
     (and_(SalesTable.when_in_unix_time >= sun_mon_start, SalesTable.when_in_unix_time <= sun_mon_end), '周日-周一'),
     # 其余4个区间的条件...
    ]
)

# 后面的分组查询逻辑和方案1一致
query = db.query(
    interval_label.label('interval'),
    func.count(SalesTable.id).label('sales_count')
).group_by('interval').order_by('interval')

results = query.all()

补充:处理空区间(保证所有6个区间都有结果)

如果某个区间没有销售数据,上面的查询不会返回该区间的行。如果你的图表需要显示所有6个区间(哪怕销售数为0),可以用左连接的方式,先生成所有区间的列表,再关联统计结果:

from sqlalchemy.sql import text

# 生成包含所有6个区间的临时数据集
all_intervals = db.query(text("""
    SELECT '周六-周日' AS interval UNION ALL
    SELECT '周日-周一' AS interval UNION ALL
    SELECT '周一-周二' AS interval UNION ALL
    SELECT '周二-周三' AS interval UNION ALL
    SELECT '周三-周四' AS interval UNION ALL
    SELECT '周四-周五' AS interval
""")).subquery()

# 先获取统计结果的子查询
stats_subquery = db.query(
    interval_label.label('interval'),
    func.count(SalesTable.id).label('sales_count')
).filter(
    SalesTable.when_in_unix_time >= overall_start_time,
    SalesTable.when_in_unix_time <= overall_end_time
).group_by('interval').subquery()

# 左连接,保证所有区间都被返回,空区间用0填充
final_query = db.query(
    all_intervals.c.interval,
    func.coalesce(stats_subquery.c.sales_count, 0).label('sales_count')
).outerjoin(stats_subquery, all_intervals.c.interval == stats_subquery.c.interval).order_by(all_intervals.c.interval)

full_results = final_query.all()

这样返回的full_results里,每个区间都会有对应的销售数,没有销售的区间会显示0,完美适配绘图需求。

内容的提问来源于stack exchange,提问作者Amin Pial

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 12:22:54