我的代码中有一个重复模式,其中模型具有跟踪其历史/状态的相关模型(一对多)。此相关模型可以包含许多对象,这些对象表示模型状态的时间点快照。
例如:
class Profile(models.Model):
pass
class Subscription(models.Model):
profile = models.ForeignKey(Profile)
data_point = models.IntegerField()
created = models.DateTimeField(default=datetime.datetime)
#Example objects
p = Provile()
subscription1 = Subscription(profile=p, data_point=32, created=datetime.datetime(2011, 7 1)
subscription2 = Subscription(profile=p, data_point=2, created=datetime.datetime(2011, 8 1)
subscription3 = Subscription(profile=p, data_point=3, created=datetime.datetime(2011, 9 1)
subscription4 = Subscription(profile=p, data_point=302, created=datetime.datetime(2011, 10 1)
我经常需要查询这些模型,以查找在过去3天内没有订阅更新或类似情况的所有“配置文件”对象。我一直在使用子选择查询来实现这个目标:
q = Subscription.objects.filter(created__gt=datetime.datetime.now()-datetime.timedelta(days=3).values('id').query
Profile.objects.exclude(subscription__id__in=q).distinct()
问题是,当涉及大表时,这非常慢。对于像这样的查询,是否有更高效的模式?也许某种方式让Django使用JOIN而不是SUBSELECT(似乎摆脱所有那些内部嵌套循环会有帮助)?
我会喜欢使用ORM,但是如果需要的话我会愿意使用.extra()方法甚至是原始SQL,如果性能提升足够引人注目的话。
我正在反对Django 1.4alpha(SVN Trunk)和Postgres 9.1。
答案 0 :(得分:0)
from django.db.models import Max
from datetime import datetime, timedelta
Profile.objects.annotate(last_update=Max('subscription__created')).filter(last_update__lt=datetime.now()-timedelta(days=3))
聚合(和注释)非常棒,请参阅:https://docs.djangoproject.com/en/dev/topics/db/aggregation/
答案 1 :(得分:0)
将数据库索引添加到created
:
created = models.DateTimeField(default=datetime.datetime, db_index=True)
根据经验,在查询或排序查询中使用的任何列都应编入索引,除非您在编写操作时很重要(在这种情况下,您应该考虑使用单独的搜索索引)。
使用没有索引的db列进行查询的速度非常快。如果要更详细地分析查询瓶颈,请打开更长时间运行语句的日志记录(例如200毫秒及以上),并对长时间运行的查询执行explain analyze
(postgres)。
编辑:
我现在才在你的评论中看到你在这个领域有一个索引。在这种情况下,更有理由查看explain analyze
的输出。
请参阅 - 关于查询计划http://www.postgresql.org/docs/current/static/runtime-config-query.html
也许这有助于作为介绍:http://blog.it-agenten.com/2015/11/tuning-django-orm-part-2-many-to-many-queries/