【问题标题】:Django Query OptimisationDjango 查询优化
【发布时间】:2013-07-26 01:43:10
【问题描述】:

我目前正在从事电信分析项目和查询优化方面的新手。要在浏览器中显示结果需要 完整 分钟,而仅要访问 45,000 条记录。您能否建议减少显示结果的时间的方法。

我写了以下查询来查找 a 年龄组的人的通话时长:

    sigma=0
    popn=len(Demo.objects.filter(age_group=age))
    card_list=[Demo.objects.filter(age_group=age)[i].card_no
                for i in range(popn)]
    for card in card_list:
        dic=Fact_table.objects.filter(card_no=card.aggregate(Sum('duration'))
        sigma+=dic['duration__sum']
    avgDur=sigma/popn

上面的代码在 for 循环中迭代年龄组。

型号如下:

class Demo(models.Model):
    card_no=models.CharField(max_length=20,primary_key=True)
    gender=models.IntegerField()
    age=models.IntegerField()
    age_group=models.IntegerField()



class Fact_table(models.Model):
    pri_key=models.BigIntegerField(primary_key=True)
    card_no=models.CharField(max_length=20)
    duration=models.IntegerField()
    time_8bit=models.CharField(max_length=8)
    time_of_day=models.IntegerField()
    isBusinessHr=models.IntegerField()
    Day_of_week=models.IntegerField()
    Day=models.IntegerField()

谢谢

【问题讨论】:

    标签: database django django-models


    【解决方案1】:

    试试看:

    sigma=0
    demo_by_age = Demo.objects.filter(age_group=age);
    
    popn=demo_by_age.count() #One
    
    card_list = demo_by_age.values_list('card_no', flat=True) # Two
    
    dic = Fact_table.objects.filter(card_no__in=card_list).aggregate(Sum('duration') #Three
    sigma = dic['duration__sum']
    
    avgDur=sigma/popn
    

    【讨论】:

    • 感谢您的耐心等待。我从未在几行 OP 代码中看到过这么多的反模式。
    • 应该#2 是:card_list = Demo.objects.filter(age_group=age).values_list('card_no', flat=True)sigma = dic['duration__sum']中缺少一个引号
    • 您的第二个查询 (card_list = demo_by_age.values_list('card_no', flat=True) # Two) 实际上从未执行过,而是在执行第三个查询时用作嵌套的 select 语句。
    【解决方案2】:

    card_list=[Demo.objects.filter(age_group=age)[i].card_no for i in range(popn)] 这样的语句将生成popn 单独的查询和数据库命中。 for-loop 中的查询也将访问数据库popn 次。作为一般规则,您应该尽量减少您使用的查询量,并且您应该只选择您需要的记录。

    只需对您的代码进行一些调整,只需一个查询即可完成。

    • 通常不需要手动指定primary_key,除了一些非常特殊的情况外,最好不要定义任何内容。 Django 自动添加一个索引的、自动增量的主键字段。如果您需要 card_no 字段作为唯一字段,并且需要根据该字段查找行,请使用:

      class Demo(models.Model):
          card_no = models.SlugField(max_length=20, unique=True)
          ...
      

      SlugField 自动将数据库索引添加到列,本质上通过该字段进行选择的速度与当它是主键时一样快。这仍然允许其他方式访问表,例如外键(我将在下一点解释),使用 Django 指定的(稍微)更快的整数字段,并将简化模型在 Django 中的使用。

    • 如果您需要将对象与另一个表中的对象关联,请使用models.ForeignKey。 Django 为您提供了一整套新功能,不仅可以更轻松地使用模型,还可以通过在 SQL 查询中使用 JOIN 子句来更快地进行大量查询。所以以你为例:

      class Fact_table(models.Model):
          card = models.ForeignKey(Demo, related_name='facts')
          ...
      

      related_name 字段允许您通过在 Django 中使用 instance.facts 访问与 Demo 实例相关的所有 Fact_table 对象。 (见https://docs.djangoproject.com/en/dev/ref/models/fields/#module-django.db.models.fields.related

    通过这两项更改,您的查询(包括对不同年龄组的循环)可以更改为超快的一键查询,为您提供每个年龄组进行的平均通话时长:

    age_groups = Demo.objects.values('age_group').annotate(duration_avg=Avg('facts__duration'))
    for group in age_groups:
        print "Age group: %s - Average duration: %s" % group['age_group'], group['duration_avg']
    

    .values('age_group') 仅从 Demo 的数据库表中选择 age_group 字段。 .annotate(duration_avg=Avg('facts__duration')) 获取来自 values 的每个唯一结果(因此每个唯一的 age_group),并且对于每个唯一结果将获取与该 age_group 中的任何 Demo 对象相关的所有 Fact_table 对象,并计算所有持续时间字段的平均值 - 全部在一个单个查询。

    【讨论】:

    • 我相应地编辑了模型和 mysql 表,但在 duration_avg 中只有“无”值出现。实际上,“无”仅在一种情况下出现,而在其他情况下则不出现。如果面对“无”,注释不起作用,为什么它会影响其他年龄组?反正优化策略挺好的。
    • @Bishal 这很奇怪,因为我自己项目的快速测试表明None 结果不应该影响其他结果。您可以更新问题中的代码吗?
    猜你喜欢
    • 2011-10-08
    • 2012-03-19
    • 2018-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-15
    • 2016-07-15
    相关资源
    最近更新 更多