Django 1.11:如何筛选满足最近两次事件时间差条件的Actor?
解决方法:用Window函数高效筛选符合时间差条件的Actor
我来帮你搞定这个问题!要筛选出最近两次事件时间差满足指定条件的Actor,**Window函数(LAG/LEAD)**是最简洁高效的方案,比嵌套Subquery或者复杂的ExpressionWrapper更直观,尤其适合数据量大的场景。
核心思路
我们需要给每个Actor的事件按时间倒序排列,然后获取每个事件的「前一次事件时间」(也就是最近的上一次事件),计算两者的时间差,最后筛选出时间差符合要求的Actor即可。
具体实现代码
1. 给事件标注前一次事件的时间并计算时间差
首先,我们用Lag()窗口函数,按Actor分组、事件时间倒序,获取每个事件的前一次事件时间,再计算时间差:
from django.db.models import Window, F from django.db.models.functions import Lag from datetime import timedelta # 给每个事件添加前一次事件的时间,并计算时间差 events_with_time_diff = Event.objects.annotate( # 按Actor分组,按事件时间倒序,获取前一次事件的时间 prev_happened_datetime=Window( expression=Lag('happened_datetime'), partition_by=['actor'], order_by=F('happened_datetime').desc() ) ).annotate( # 计算当前事件与前一次事件的时间差 time_diff=F('happened_datetime') - F('prev_happened_datetime') )
2. 筛选符合时间差条件的Actor
接下来,我们只需要筛选出时间差符合要求的记录,再关联到Actor模型去重即可:
# 示例1:获取最近两次事件间隔不超过10天的Actor target_actors = Actor.objects.filter( pk__in=events_with_time_diff.filter( time_diff__lte=timedelta(days=10), prev_happened_datetime__isnull=False # 排除只有1次事件的Actor ).distinct('actor').values('actor__pk') ) # 示例2:获取最近两次事件间隔不少于5小时的Actor target_actors = Actor.objects.filter( pk__in=events_with_time_diff.filter( time_diff__gte=timedelta(hours=5), prev_happened_datetime__isnull=False ).distinct('actor').values('actor__pk') )
为什么你的之前的Subquery尝试效果不佳?
你之前的代码里用latest('happened_datetime')会导致每个Event都去查询该Actor的最新事件,而不是当前Event的「前一次事件」——这会导致时间差计算错误(比如拿当前事件和整个Actor的最新事件比,而不是相邻的前一次),而且重复查询会极大降低效率,数据量大的时候问题更明显。
优化建议
为了让查询更高效,建议给Event表添加联合索引:
class Event(models.Model): actor = models.ForeignKey(Actor, null=False, blank=False, on_delete=models.PROTECT) happened_datetime = models.DateTimeField(_('When did the event took place?'), blank=False, null=False) class Meta: indexes = [ # 按Actor和事件时间的联合索引,大幅提升Window函数的分组排序效率 models.Index(fields=['actor', 'happened_datetime']), ]
内容的提问来源于stack exchange,提问作者Hal
相关产品推荐
相关产品推荐

