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

如何在Django中按账户聚合Decimal字段并实现条件求和?

Django实现按ACCOUNT分组聚合并关联原数据的问题

需求说明

按ACCOUNT字段分组,完成以下聚合计算,并将聚合结果关联到每一行原始数据中:

  • 对LIMIT、MARKET、LENDING字段分别求和,别名依次为bond_limit、market_value、lending_value
  • 对BALANCE字段做条件求和:大于0的部分求和为FREE,小于0的部分求和为MISS

参考SQL实现

SELECT 
    ACCOUNT,
    LIMIT,
    MARKET,
    LENDING,
    BALANCE,
    SUM(LIMIT) OVER (PARTITION BY ACCOUNT) AS bond_limit,
    SUM(MARKET) OVER (PARTITION BY ACCOUNT) AS market_value,
    SUM(LENDING) OVER (PARTITION BY ACCOUNT) AS lending_value,
    SUM(CASE WHEN BALANCE > 0 THEN BALANCE ELSE 0 END) OVER (PARTITION BY ACCOUNT) AS FREE,
    SUM(CASE WHEN BALANCE < 0 THEN BALANCE ELSE 0 END) OVER (PARTITION BY ACCOUNT) AS MISS
FROM your_table;

原始数据与预期结果

原始数据

ACCOUNTLIMITMARKETLENDINGBALANCE
1000010200.000.00-234.551000.00
1000010300.0011.000.00-239.00
1000010-200.00235.00-134.00450.00
100001130.001.00-10.00-98.00
1000011-200.00235.00-134.0049.00

预期结果

ACCOUNTLIMITMARKETLENDINGBOND_LIMITMARKET_VALUELENDING_VALUEFREEMISS
1000010200.000.00-234.55300.00246.00-368.551450.00-239.00
1000010300.0011.000.00300.00246.00-368.551450.00-239.00
1000010-200.00235.00-134.00300.00246.00-368.551450.00-239.00
100001130.001.00-10.00-170.00236.00-144.0049.00-98.00
1000011-200.00235.00-134.00-170.00236.00-144.0049.00-98.00

遇到的问题

编写的Django代码出现NameError,且不清楚如何实现BALANCE的条件聚合:

# 错误代码1:aggregate返回字典,无法链式调用filter
erg = database.objects.filter(
    account=ACCOUNT
).aggregate(
    bond_limit=Sum('LIMIT'),
    market_value=Sum('MARKET'),
    lending_value=Sum('LENDING')
).filter(bond_limit=bond_limit).aggregate(bond_limit=Sum('LIMIT')
                        ^^^^^^^^^^^
NameError: name 'bond_limit' is not defined
# 错误尝试:不知道如何实现BALANCE的条件求和
erg.filter(BALANCE__gt=0)... # 无思路

解决方案

要实现每行数据关联分组聚合结果,需使用Django的Window函数(对应SQL的窗口函数),配合Case/When实现条件求和。

完整代码示例

from django.db.models import Sum, Case, When, FloatField
from django.db.models.expressions import Window
from django.db.models.functions import Coalesce

# 执行查询,为每行添加分组聚合字段
results = database.objects.annotate(
    # 按ACCOUNT分组求和LIMIT
    bond_limit=Window(
        expression=Sum('LIMIT'),
        partition_by=['ACCOUNT']
    ),
    # 按ACCOUNT分组求和MARKET
    market_value=Window(
        expression=Sum('MARKET'),
        partition_by=['ACCOUNT']
    ),
    # 按ACCOUNT分组求和LENDING
    lending_value=Window(
        expression=Sum('LENDING'),
        partition_by=['ACCOUNT']
    ),
    # 按ACCOUNT分组,求和大于0的BALANCE
    FREE=Window(
        expression=Coalesce(Sum(Case(
            When(BALANCE__gt=0, then='BALANCE'),
            default=0,
            output_field=FloatField()
        )), 0),
        partition_by=['ACCOUNT']
    ),
    # 按ACCOUNT分组,求和小于0的BALANCE
    MISS=Window(
        expression=Coalesce(Sum(Case(
            When(BALANCE__lt=0, then='BALANCE'),
            default=0,
            output_field=FloatField()
        )), 0),
        partition_by=['ACCOUNT']
    )
).values(
    'ACCOUNT', 'LIMIT', 'MARKET', 'LENDING', 
    'bond_limit', 'market_value', 'lending_value', 'FREE', 'MISS'
)

# 遍历结果
for item in results:
    print(item)

关键说明

  1. Window函数:通过partition_by=['ACCOUNT']实现按账号分组,将聚合结果关联到每一行,而非合并分组。
  2. 条件求和:用Case+When实现类似SQL的case when逻辑,再用Sum包裹完成条件求和;Coalesce用于将可能的NULL结果转为0。
  3. 避免错误:aggregate是对整个查询集做单一聚合(返回字典),无法实现分组后关联每行的需求,必须使用annotate+Window组合。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 08:24:56