Django Query 每天将两行中的值聚合为单个结果
Posted
技术标签:
【中文标题】Django Query 每天将两行中的值聚合为单个结果【英文标题】:Django Query to aggregate values from two rows into single result per day 【发布时间】:2017-04-05 22:18:12 【问题描述】:我正在尝试创建一个模型管理器查询,该查询返回给定日期范围内多种余额类型(DATA 和 AIRTIME)按天分组的结果。余额历史表会随着 SIM 卡使用数据而一直更新,但对于报告,我们只想每天显示一个余额
模型很简单:
class Sim(TimeStampedModel):
number = models.CharField()
class SimBalanceHistory(TimeStampedModel):
balance_type = models.CharField(choices=BALANCE_TYPES, max_length=10)
amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
sim = models.ForeignKey(Sim, related_name='balance_histories')
SimBalanceHistory 表中的一些示例数据:
ID BALANCE_TYPE AMOUNT SIM_ID CREATED MODFIED
1603 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1604 DATA 36.75 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1703 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1704 DATA 36.74 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1803 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1804 DATA 36.73 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1973 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1974 DATA 36.72 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2059 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2060 DATA 36.72 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2135 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2136 DATA 36.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2229 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2230 DATA 36.70 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159
440026 DATA 34.26 348 2016-11-18 23:34:36.976777 +02:00 2016-11-18 23:34:36.976836 +02:00
440885 AIRTIME 3.71 348 2016-11-18 23:57:57.448809 +02:00 2016-11-18 23:57:57.448878 +02:00
440889 DATA 34.25 348 2016-11-18 23:57:58.854901 +02:00 2016-11-18 23:57:58.854959 +02:00
443590 AIRTIME 3.71 348 2016-11-19 00:35:07.598679 +02:00 2016-11-19 00:35:07.598755 +02:00
443593 数据 34.24 348 2016-11-19 00:35:08.991217 +02:00 2016-11-19 00:35:08.991266
目前查询如下:
def daily_balances(self, start_date, end_date):
return self.filter(
created__range=[start_date, end_date]
).dates(
'created',
'day',
order='DESC'
).order_by(
'-created'
).distinct(
'created', 'balance_type'
).values(
'created',
'amount',
'balance_type'
)
按天限制,但为每个 balance_type 返回一行
'balance_type': 'AIRTIME', 'created': datetime.datetime(2016, 11, 22, 0, 0, tzinfo=<UTC>), 'amount': Decimal('5.00')
'balance_type': 'DATA', 'created': datetime.datetime(2016, 11, 22, 0, 0, tzinfo=<UTC>), 'amount': Decimal('12.00')
我试图在查询集的结果中得到类似这样的结果(每天 1 条记录,其值为通话时间量和数据量:
'created': datetime.datetime(2016, 11, 22, 0, 0, tzinfo=<UTC>), 'data_amount': Decimal('5.00'), 'airtime_amount': Decimal('12.00')
'created': datetime.datetime(2016, 11, 21, 0, 0, tzinfo=<UTC>), 'data_amount': Decimal('6.00'), 'airtime_amount': Decimal('14.00')
【问题讨论】:
【参考方案1】:我认为您现有的查询已经很不错了,但是如果您真的希望每天有一行的余额,您可以使用conditional aggregates:
from django.db.models import IntegerField, F, Sum, When
SimBalanceHistory.objects\
.filter(created__range=[start_date, end_date])\
.dates('created', 'day', order='DESC')\
.values('created')\
.annotate(airtime_amount=Sum(Case(When(balance_type='AIRTIME', then=F('amount')), output_field=DecimalField())),
data_amount=Sum(Case(When(balance_type='DATA', then=F('amount')), output_field=DecimalField())))
【讨论】:
感谢这是史诗【参考方案2】:这是我会尝试的。
首先,在每行中使用特定于其类型的名称重命名金额:data_amount 和 airtime_amount。 然后,在计算每个金额的总和时按“已创建”对行进行分组。
所以,我认为你可以做类似的事情
from django.db.models import F, Sum
def daily_balances(self, start_date, end_date):
airtime_records = SimBalanceHistory.objects.filter(
created__range=[start_date, end_date],
balance_type='AIRTIME'
).annotate(airtime_amount=F('amount'))
data_records = SimBalanceHistory.objects.filter(
created__range=[start_date, end_date],
balance_type='DATA'
).annotate(data_amount=F('amount'))
return (airtime_records | data_records).dates(
'created',
'day',
order='DESC'
).order_by(
'-created'
).values('created').annotate(
Sum('data_amount'),
Sum('airtime_amount'),
)
该代码进行了多次查询,但我想不出一个解决方案只在一个中执行此操作。希望有人会发布更好的答案,但也许这可以帮助你。
参考。
How to query as GROUP BY in django? How to rename items in values() in Django? Howto merge 2 Django QuerySets in one and make a SELECT DISTINCT 聚合和注释: https://docs.djangoproject.com/en/1.10/topics/db/aggregation/【讨论】:
感谢您的帮助,当我尝试合并两个查询集时,由于前两个查询中金额字段的名称不同,因此无法合并。以上是关于Django Query 每天将两行中的值聚合为单个结果的主要内容,如果未能解决你的问题,请参考以下文章