Django queryset:如何更改返回的数据结构

问题描述 投票:1回答:1

这个问题与游戏拱廊客厅有关,人们在客厅里玩游戏。当一个人玩时,在数据库中创建了一个新条目。我的模型是这样的:

class gaming_machine(models.Model):
  machine_no = models.Integer()
  score = models.Integer()
  created = models.DateTimeField(auto_now_add=True)

我的观点是这样的:

today = datetime.now().date()

# i am querying the db for getting the gaming_machine objects where score = 192 or 100 and the count of these objects separately for gaming_machines object which have 192 score and gaming_machine objects which have score as 100 

gaming_machine.objects.filter(Q(points=100) | Q(points=192),created__startswith=today).values_list('machine_no','points').annotate(Count('machine_no'))
# this returns a list of tuples -> (machine_no, points, count)
<QuerySet [(330, 192,2), (330, 100,4), (331, 192,7),(331,192,8)]>
  1. 我可以将返回的查询集格式更改为:{(330, 192):2, (330, 100) :4, (331, 192):7,(331,192):8} # that is a dictionary with a key as a tuple consisting (machine_no,score) and value as count of such machine_nos
  2. 我知道我可以使用像字典理解这样的东西来更改python端的这个查询集的格式,但我不能这样做,因为这需要大约1.4秒的时间来完成,因为django查询集是懒惰的。
python django django-views
1个回答
1
投票

Django's lazy queries...

但我无法做到这一点,因为这需要大约1.4秒的时间来完成,因为django查询集是懒惰的。

Django的查询集的懒惰实际上(接近)对性能没有影响。它们是懒惰的,因为它们推迟查询数据库直到你需要结果(例如当你开始迭代它时)。但随后他们将获取所有行。因此,每次获取下一行时都没有开销,所有行都被提取,然后Python会非常快速地迭代它。

因此,懒惰不是逐行的:每次想要获取下一行时,它都不会使光标前进。因此(非常)限制与数据库的通信。

... and why it does not matter (performance-wise)

除非行数很大(50'000或更多),否则转换到字典也应该相当快。所以我怀疑开销可能是由于查询本身造成的。特别是因为Django必须“反序列化”元素:将响应转换为元组,所以虽然可能会有一些额外的开销,但与没有字典理解的工作相比,它通常是合理的。通常,如果查询中的任务导致传输到Python的数据较少,则会对其进行编码。

例如,通过在数据库中执行计数,数据库将通过过滤返回每行而不是几行的整数,我们也减少行数(因为通常不是所有行都匹配给定的标准)。此外,数据库通常具有快速查找机制,可以提升WHEREs,GROUP BYs,ORDER BYs等。但是,将流后处理到不同的对象通常会花费相同的数据库时间。

所以字典理解应该做:

{
    d[:2]: d[3]
    for d in gaming_machine.objects.filter(
                  Q(points=100) | Q(points=192),created__startswith=today
             ).values_list(
                 'machine_no','points'
             ).annotate(
                 Count('machine_no')
             )
}

Speeding up queries

由于问题可能位于数据库中,因此您可能需要考虑加速的一些可能性。

建立索引

通常,提高查询性能的最佳方法是在经常过滤的列上构建索引,并且具有大量不同的值。

在这种情况下,数据库将构造一个数据结构,该数据结构为该列的每个值存储与该值匹配的行列表。因此,数据库不是通读所有行并选择相关的行,而是可以立即访问数据结构,并且通常在合理的时间内知道哪些行具有该值。

请注意,这通常仅在列包含大量不同值时才有用:例如,列仅包含两个值(在1%的情况下,值为0,99%的情况为1)并且我们过滤在一个非常常见的值上,这不会产生太多的加速,因为我们需要处理的集合具有大致相同的大小。

因此,根据值的不同,我们可以向pointscreated字段添加索引:

class gaming_machine(models.Model):
  machine_no = models.Integer()
  score = models.Integer(db_index=True)
  created = models.DateTimeField(auto_now_add=True, db_index=True)

改进查询

其次,我们也可以旨在改进查询本身,虽然这可能更依赖于数据库(如果我们有两个查询q1q2,那么q1可能比MySQL数据库上的q2工作得更快,而q2可以更快地工作比PostgreSQL数据库上的q1)。所以这很棘手:当然,有些事情通常会起作用,但很难给出保证。

例如,saztimes x IN (100, 192)的工作速度比x = 100 OR x = 192快(见here)。此外,你在这里使用__startswith,它可能表现良好 - 取决于数据库如何存储时间戳 - 但如果它首先需要转换datetime,它可能导致计算成本高昂的查询。无论如何,使用created__date更具声明性,因为它清楚地表明你希望created的日期等于今天,所以更有效的查询可能是:

{
    d[:2]: d[3]
    for d in gaming_machine.objects.filter(
                  points__in=[100, 192], created__date=today
             ).values_list(
                 'machine_no','points'
             ).annotate(
                 Count('machine_no')
             )
}
© www.soinside.com 2019 - 2024. All rights reserved.