我正在通过提供年初至今统计数据的 API 填充我的数据库,并且我将每天多次从该 API 中提取数据。使用年初至今的统计数据,我需要生成每月和每周的统计数据。我目前正在尝试通过从月底的统计数据中减去月初的统计数据并将其保存在单独的模型中来实现这一点,但是这个过程花费的时间太长了,我需要它更快.
我的模型看起来像这样:
class Stats(models.Model):
date = models.DateField(default=timezone.now) # Date pulled from API
customer_id = models.IntegerField(default=0) # Simplified for this example
a = models.IntegerField(default=0)
b = models.IntegerField(default=0)
c = models.IntegerField(default=0)
d = models.IntegerField(default=0)
class Leaderboard(models.Model):
duration = models.CharField(max_length=7, default="YEARLY") # "MONTHLY", "WEEKLY"
customer_id = models.IntegerField(default=0)
start_stats = models.ForeignKey(Stats, related_name="start_stats") # Stats from the start of the Year/Month/Week
end_stats = models.ForeignKey(Stats, related_name="end_stats") # Stats from the end of the Year/Month/Week
needs_update = models.BooleanField(default=False) # set to True only if the end_stats changed (likely when a new pull happened)
a = models.IntegerField(default=0)
b = models.IntegerField(default=0)
c = models.IntegerField(default=0)
d = models.IntegerField(default=0)
e = models.IntegerField(default=0) # A value computed based on a-d, used to sort Leaderboards
我以为我可以使用 Leaderboard.objects.filter(needs_update=True).update(a=F("end_stats__a")-F("start_stats__a"), ...)
,但这给了我一个错误“此查询中不允许加入字段引用”。
我目前正在遍历 QuerySet Leaderboard.objects.filter(needs_update=True)
,执行减法操作并保存(全部使用 @transaction.atomic
),但是以这种方式处理的约 380,000 条测试记录只用了一个多小时,所以我怀疑这种方式对于我需要的东西来说太慢了。
如果不同的格式可以帮助这个排行榜更新更快,我可以改变我存储数据的方式(也许在提取数据时做减法并保存每日增量?),但我觉得我一直在赶时间想什么就做什么,而不知道在这种情况下我应该做什么。非常感谢此时的任何反馈。
最佳答案
经过大量修改,我想我找到了一种适用于这种情况的方法。我的测试样本比以前小(84,600 条记录),但它在 8 秒内完成 - 大约每秒 10,575 条记录(与我之前测试的大约每秒 6,300 条记录相比)。
可能有一种方法可以进一步完善它,但这是我正在做的:
from django.db.models import F, Subquery, OuterRef
# Get the latest versions of the stats
Leaderboard.objects.filter(needs_update=True).update(
a=Subquery(Stats.objects.filter(pk=OuterRef('end_stats')).values('a')[:1]),
b=Subquery(Stats.objects.filter(pk=OuterRef('end_stats')).values('b')[:1]),
c=Subquery(Stats.objects.filter(pk=OuterRef('end_stats')).values('c')[:1]),
d=Subquery(Stats.objects.filter(pk=OuterRef('end_stats')).values('d')[:1])
)
# Subtract the earliest versions of the stats
Leaderboard.objects.filter(needs_update=True).update(
a=F('a') - Subquery(Stats.objects.filter(pk=OuterRef('start_stats')).values('a')[:1]),
b=F('b') - Subquery(Stats.objects.filter(pk=OuterRef('start_stats')).values('b')[:1]),
c=F('c') - Subquery(Stats.objects.filter(pk=OuterRef('start_stats')).values('c')[:1]),
d=F('d') - Subquery(Stats.objects.filter(pk=OuterRef('start_stats')).values('d')[:1])
)
# Calculate stats that require earlier stats.
Leaderboard.objects.filter(needs_update=True).update(
e=F('a') + F('b') * F('c') / F('d'),
needs_update=False
)
我觉得应该有一种方法每次更新只使用一个子查询
,这应该会进一步提高速度。
关于python - 需要快速更新 Django 模型与其他两个模型之间的差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53789518/