10

我有一个名为 Valor 的模型。Valor 有一个机器人。我这样查询:

Valor.objects.filter(robot=r).reverse()[0]

获得最后的Valor the r机器人。Valor.objects.filter(robot=r).count() 大约是 200000,在我的 PC 中获取最后一个项目大约需要 4 秒。

我怎样才能加快速度?我查询的方式不对?

4

9 回答 9

8

此问题的最佳 mysql 语法将类似于以下内容:

SELECT * FROM table WHERE x=y ORDER BY z DESC LIMIT 1

与此等效的 django 将是:

Valor.objects.filter(robot=r).order_by('-id')[:1][0]

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

于 2011-11-30T15:15:26.330 回答
7

如果之前的建议都不起作用,我建议将 Django 排除在外,并针对您的数据库运行此原始 sql。我猜你的表名,所以你可能需要相应地调整:

SELECT * FROM valor v WHERE v.robot_id = [robot_id] ORDER BY id DESC LIMIT 1;

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

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

qs = Valor.objects.filter(robot=r).order_by('-id')[0]
print qs.query

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

于 2009-12-05T17:26:18.283 回答
4

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

class Robot(models.Model):
    # ...
    last_valor = models.ForeignKey('Valor', null=True, blank=True)

然后使用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)

创建 Valor 对象时,您将支付额外的 db 事务的成本,但 last_valor 查找速度会非常快。试一试,看看这个折衷对您的应用程序是否值得。

于 2009-12-05T18:01:10.723 回答
3

好吧,没有 order_by 子句,所以我想知道您所说的“最后一个”是什么意思。假设您的意思是“最后添加”,

Valor.objects.filter(robot=r).order_by('-id')[0]

可能会为您完成这项工作。

于 2009-12-05T17:08:29.763 回答
3

django 1.6 引入了 .first() 和 .last():

https://docs.djangoproject.com/en/1.6/ref/models/querysets/#last

所以你可以简单地做:

Valor.objects.filter(robot=r).last()
于 2014-07-16T12:00:43.853 回答
1

相当快也应该是:

qs = Valor.objects.filter(robot=r) # <-- it doesn't hit the database
count = qs.count()                 # <-- first hit the database, compute a count
last_item = qs[ count-1 ]          # <-- second hit the database, get specified rownum

因此,实际上您只执行 2 个 SQL 查询;)

于 2012-05-19T08:58:59.660 回答
0

django中有限制条款吗?这样您就可以拥有数据库,只需返回一条记录。

mysql

 select * from table where x = y limit 1

sql服务器

 select top 1 * from table where x = y

甲骨文

 select * from table where x = y and rownum = 1

我意识到这没有翻译成 django,但有人可以回来清理它。

于 2009-12-05T17:13:13.267 回答
0

这样做的正确方法是使用内置的 QuerySet 方法 latest() 并将其提供给它应该排序的任何列(字段名称)。缺点是它只能按单个 db 列排序。

当前的实现看起来像这样,并且在与@Aaron 的建议相同的意义上进行了优化。

def latest(self, field_name=None):
    """
    Returns the latest object, according to the model's 'get_latest_by'
    option or optional given field_name.
    """
    latest_by = field_name or self.model._meta.get_latest_by
    assert bool(latest_by), "latest() requires either a field_name parameter or 'get_latest_by' in the model"
    assert self.query.can_filter(), \
            "Cannot change a query once a slice has been taken."
    obj = self._clone()
    obj.query.set_limits(high=1)
    obj.query.clear_ordering()
    obj.query.add_ordering('-%s' % latest_by)
    return obj.get()
于 2014-03-12T21:56:19.747 回答
0
Model_Name.objects.first()

//获取第一个元素

Model_name.objects.last()

//获取最后一个()

在我的情况下,最后一个不起作用,因为数据库中只有一行可能对你也有帮助:)

于 2015-02-09T15:00:37.900 回答