python - 是否可以分别对多个列进行 GROUP BY 并使用 django ORM 通过其他列聚合它们中的每一个?

标签 python django django-queryset django-aggregation

我知道怎么做 GROUP BY并汇总:

>>> from expenses.models import Expense
>>> from django.db.models import Sum
>>> qs = Expense.objects.order_by().values("is_fixed").annotate(is_fixed_total=Sum("price"))
>>> qs
<ExpenseQueryset [{'is_fixed': False, 'is_fixed_total': Decimal('1121.74000000000')}, {'is_fixed': True, 'is_fixed_total': Decimal('813.880000000000')}]>
但是,如果我想对其他两列做同样的事情,它只返回最后一个:
>>> qs = (
...     Expense.objects.order_by()
...     .values("is_fixed")
...     .annotate(is_fixed_total=Sum("price"))
...     .values("source")
...     .annotate(source_total=Sum("price"))
...     .values("category")
...     .annotate(category_total=Sum("price"))
... )
>>> qs
<ExpenseQueryset [{'category': 'FOOD', 'category_total': Decimal('33.9000000000000')}, {'category': 'GIFT', 'category_total': Decimal('628')}, {'category': 'HOUSE', 'category_total': Decimal('813.880000000000')}, {'category': 'OTHER', 'category_total': Decimal('307')}, {'category': 'RECREATION', 'category_total': Decimal('100')}, {'category': 'SUPERMARKET', 'category_total': Decimal('52.8400000000000')}]>
可以只用一个查询而不是三个查询来完成我想要的吗?
预期结果:
<ExpenseQueryset [{'category': 'FOOD', 'total': Decimal('33.9000000000000')}, {... all other categories ...}, 
{'source': 'MONEY', 'total': Decimal('100')}, {... all other sources ...}, {'is_fixed': False, 'total': Decimal('1121.74000000000')}, {'is_fixed': True, 'total': Decimal('813.880000000000')}]>
最理想的情况是,它可以拆分为以下内容:
<ExpenseQueryset ['categories': [{'category': 'FOOD', 'total': Decimal('33.9000000000000')}, {... all other categories ...}], 
'sources': [{'source': 'MONEY', 'total': Decimal('100')}, {... all other sources ...}], 'type': [{'is_fixed': False, 'total': Decimal('1121.74000000000')}, {'is_fixed': True, 'total': Decimal('813.880000000000')}]]>
但这只是一个很大的好处。

最佳答案

答案是否定的,因为 SQL 是不可能的
但是您可以将以下方法与 python 编码结合使用:
我认为即使在原始 SQL 中也不可能,因为在每个查询中,您可以将一个或多个字段组合在一起,但不能将每个字段的结果分开。
但是可以通过一个查询来完成,并使用少量 Python 代码以您想要的格式合并结果 .下面我描述了如何逐步使用它。并在下一节中编写了一个 python 方法,您可以动态地将其用于任何进一步的用途。
这个怎么运作
我能提到的唯一简单的解决方案是,您按所需的 3 个字段进行分组,然后进行简单的 Python 编程以将每个字段的结果汇总在一起。
在这种方法中,你将只有 一问但每个字段分组的结果分开。

from expenses.models import Expense
from django.db.models import Sum

qs = Expense.objects.order_by().values("is_fixed", "source", "category").annotate(total=Sum("price"))
现在结果将如下所示:
<ExpenseQueryset [{'category': 'FOOD', 'is_fixed': False, 'source': 'MONEY', 'total': Decimal('33.9000000000000')}, { ...}, 
现在我们可以通过迭代这个结果来简单地聚合每个字段的结果
category_keys = []
for q in qs:
    if not q['category'] in category_keys:
        category_keys.append(q['category'])

# Now we have proper values of category in category_keys
category_result = []
for c in category_keys:
    value = sum(item['total'] for item in qs if item['category'] == c)
    category_result.append({'category': c, 'total': value)
category 的结果字段将是这样的:
[{'category': 'FOOD', 'total': 33.3}, {... other category results ...}
现在我们可以继续并按字段为其他组生成结果 is_fixedsource像下面这样:
source_keys = []
for q in qs:
    if not q['source'] in source_keys:
        source_keys.append(q['source'])
source_result = []
for c in source_keys:
    value = sum(item['total'] for item in qs if item['source'] == c)
    source_result.append({'source': c, 'total': value)

is_fixed_keys = []
for q in qs:
    if not q['is_fixed'] in is_fixed_keys:
        source_keys.append(q['is_fixed'])
is_fixed_result = []
for c in is_fixed_keys:
    value = sum(item['total'] for item in qs if item['is_fixed'] == c)
    is_fixed_result.append({'is_fixed': c, 'total': value)
全局解决方案
现在我们知道如何使用这个解决方案了,这里有一个函数,它可以给你想要的字段,并动态地为你提供正确的结果。
def find_group_by_separated_by_keys(key_list):
    """ key_list in this example will be:
        key_list = ['category', 'source', 'is_fixed']
    """
    qs = Expense.objects.order_by().values(*tuple(key_list)).annotate(total=Sum("price"))
    qs = list(qs)
    result = []
    for key in key_list:
        key_values = []
        for item in qs:
            if not item[key] in key_values:
                key_values.append(item[key])
        
        key_result = []
        for v in key_values:
            value = sum(item['total'] for item in qs if item[key] == v)
            key_result.append({key: v, 'total': value})

        result.extend(key_result)
    return result
现在只需在您的代码中像下面一样简单地使用它:
find_group_by_separated_by_keys(['category', 'source', 'is_fixed')
它会给出一个值列表,比如你想要的正确格式

关于python - 是否可以分别对多个列进行 GROUP BY 并使用 django ORM 通过其他列聚合它们中的每一个?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68749811/

相关文章:

python - 为什么要在 Python Flask 应用程序的 settings.py 中使用 os.getenv() ?

python - 从函数返回用户提供的值

python - Django 总是在管理索引页面中将模型的详细名称大写

php - nginx + proxy pas - 504 网关超时,1 分钟 30 秒后超时。试过 proxy_read_timeout 但没有成功

python - Django session 设置默认 key 用法

django - 如何消除循环中django查询的低效率?

python - 以自定义时间格式记录

Python Pillow 如何截取屏幕截图或将文本转换为图像

python - 执行操作并重定向到相同的 URL 不会刷新页面

django - 制作 Q 对象的正确方法,它过滤 Django QuerySet 中的所有条目?