8

我正在尝试用两个本身聚合的值来计算百分比。解释我所追求的 SQL 查询如下:

SELECT (SUM(field_a) / SUM(field_b) * 100) AS percent
FROM myapp_mymodel 
GROUP BY id
ORDER BY id

我尝试使用以下内容来构造一个 QuerySet,但不幸的是它不包含额外的字段:

MyModel.objects.values('id').annotate(
   sum_field_a=Sum('field_a'),
   sum_field_b=Sum('field_b')).extra(
      select={'percent': 'sum_field_a / sum_field_b * 100'})

让我恼火的是——根据 Django 文档——这似乎是要走的路:

当 values() 子句用于约束结果集中返回的列 […] 而不是为原始 QuerySet 中的每个结果返回带注释的结果时,原始结果将根据指定字段的唯一组合进行分组在 values() 子句中。然后为每个唯一组提供注释;注释是在组的所有成员上计算的。

来源: http ://docs.djangoproject.com/en/dev/topics/db/aggregation/#values

如果在 extra() 子句之后使用 values() 子句,则由 extra() 中的选择参数定义的任何字段都必须显式包含在 values() 子句中。但是,如果在 values() 之后使用了 extra() 子句,则 select 添加的字段将自动包含在内。

来源: http ://docs.djangoproject.com/en/dev/ref/models/querysets/#values

4

3 回答 3

3

自 Django 1.8 以来,聚合表达式允许在聚合函数上轻松使用此类表达式,而没有有问题的“extra()”方法。

qs = (
    MyModel.objects.values('id')
    .annotate(percent=Sum('field__a') / Sum('field__b') * 100)
    .order_by('id')
)
>>> print(str(qs.query))
SELECT id, ((SUM(field_a) / SUM(field_b)) * 100) AS percent
FROM app_mymodel GROUP BY id ORDER BY id ASC

(提到的问题 #15546 很快就被文档注释​​关闭了,在 values() 之后的 extra() 将不起作用 -提交 a4a250a。)

于 2017-09-05T21:53:04.320 回答
0

正如您所指出的(#15546),django 中可能存在错误。

但作为一种解决方法,您可以通过执行以下操作将实际计算的负担放在 python 而不是 SQL 数据库上:

[{'field_c': model['field_c'],
  'percent': m['sum_field_a'] * 100.0 / m['sum_field_b']}
 for model in MyModel.objects.values('field_c').annotate(
    sum_field_a=Sum('field_a'),
    sum_field_b=Sum('field_b')).order_by('field_c')]

由于此解决方案迫使您遍历所有数据,这取决于您想要做什么,它可能会或可能不会被接受。

于 2011-03-11T01:26:26.170 回答
0

如果在 extra() 子句之后使用 values() 子句,则由 extra() 中的选择参数定义的任何字段都必须显式包含在 values() 子句中。

来源:http ://docs.djangoproject.com/en/dev/ref/models/querysets/#values

在 select 中添加的“百分比”字段可以显式添加到 values 子句中,并且应该添加到查询集中。

MyModel.objects.annotate(
              sum_field_a=Sum('field_a'),
              sum_field_b=Sum('field_b')).extra(
              select={'percent': 'sum_field_a / sum_field_b * 100'}
         ).values('id', 'percent')
于 2011-03-13T14:32:05.117 回答