快速查找Django QuerySet中的最后一个元素?

Jua*_*nti 10 django optimization django-queryset

我有一个叫做Valor的模特.勇气有一个机器人.我这样查询:

Valor.objects.filter(robot=r).reverse()[0]
Run Code Online (Sandbox Code Playgroud)

让最后一个Valor成为r机器人.Valor.objects.filter(robot = r).count()约为200000,在我的电脑中获取最后一项约需4秒.

我怎样才能加快速度?我在查询错误的方式?

Aar*_*ron 8

这个问题的最佳mysql语法将是:

SELECT * FROM table WHERE x=y ORDER BY z DESC LIMIT 1
Run Code Online (Sandbox Code Playgroud)

与此相当的django将是:

Valor.objects.filter(robot=r).order_by('-id')[:1][0]
Run Code Online (Sandbox Code Playgroud)

请注意此解决方案如何利用django的切片方法编译对象列表之前限制查询集.


Joe*_*way 7

如果以前的建议都没有起作用,我建议将Django从等式中取出并对你的数据库运行这个原始的sql.我猜你的桌名,所以你可能需要相应调整:

SELECT * FROM valor v WHERE v.robot_id = [robot_id] ORDER BY id DESC LIMIT 1;
Run Code Online (Sandbox Code Playgroud)

这慢吗?如果是这样,让您的RDBMS(MySQL?)向您解释查询计划.这将告诉您它是否正在进行任何全表扫描,您显然不希望使用大表.您也可以编辑您的问题并包含valor表格的架构供我们查看.

此外,您可以通过执行此操作来查看Django生成的SQL(使用Peter Rowell提供的查询集):

qs = Valor.objects.filter(robot=r).order_by('-id')[0]
print qs.query
Run Code Online (Sandbox Code Playgroud)

确保SQL类似于我上面发布的"原始"查询.您还可以让您的RDBMS向您解释该查询计划.


ist*_*ble 4

听起来您的数据集将足够大,您可能想要稍微非规范化一些东西。您是否尝试过跟踪 Robot 对象中的最后一个 Valor 对象?

class Robot(models.Model):
    # ...
    last_valor = models.ForeignKey('Valor', null=True, blank=True)
Run Code Online (Sandbox Code Playgroud)

然后使用post_save 信号进行更新。

from django.db.models.signals import post_save

def record_last_valor(sender, **kwargs):
    if kwargs.get('created', False):
        instance = kwargs.get('instance')
        instance.robot.last_valor = instance

post_save.connect(record_last_valor, sender=Valor)
Run Code Online (Sandbox Code Playgroud)

当您创建 Valor 对象时,您将支付额外的数据库事务成本,但 last_valor 查找将非常快。尝试一下,看看这种权衡对于您的应用程序是否值得。