2012-03-14 95 views
2

我正在优化我们的(第一个)Django项目中的缓慢页面加载。整个项目不会测试状态管理,所以有些协议会有计划执行的情况。目前的代码是:Django:我如何避免不必要的SQL语句?

protocols = Protocol.active.filter(team=team, release=release) 
cases = Case.active.filter(protocol__in=protocols) 
caseCount = cases.count() 
plannedExecs = Planned_Exec.active.filter(case__in=cases, team=team, release=release) 

# Start aggregating test suite information 
# pgi Model 
testSuite['pgi_model'] = [] 
for pgi in PLM.objects.filter(release=release).values('pgi_model').distinct(): 
    plmForPgi = PLM.objects.filter(pgi_model=pgi['pgi_model']) 
    peresults = plannedExecs.filter(plm__in=plmForPgi).count() 
    if peresults > 0: 
     try: 
      testSuite['pgi_model'].append((pgi['pgi_model'], "", "", peresults, int(peresults/float(testlistCount)*100))) 
     except ZeroDivisionError: 
      testSuite['pgi_model'].append((pgi['pgi_model'], "", "", peresults, 0)) 

# Browser 
testSuite['browser'] = [] 
for browser in BROWSER_OPTIONS: 
    peresults = plannedExecs.filter(browser=browser[0]).count() 
    try: 
     testSuite['browser'].append((browser[1], "", "", peresults, int(peresults/float(testlistCount)*100))) 
    except ZeroDivisionError: 
     testSuite['browser'].append((browser[1], "", "", peresults, 0)) 

# ... more different categories are aggregated below, then the report is generated... 

该代码制作了大量的SQL语句。 PLM.objects.filter(release=release).values('pgi_model').distinct()返回一个包含50个字符串的列表,并且这两个过滤器操作都为每个字符串执行一条SQL语句,这意味着100个SQL语句仅用于此循环。 (另外,它似乎应该使用values_listflat=True。)

由于我想获取有关相关案件和计划执行信息,我想我只需要检索这两个表,然后对此进行一些分析。使用filter和count()似乎是当时显而易见的解决方案,但我想知道如果使用.values()构建相关案例和计划执行信息的代码,然后再分析它,那么是不是更好?以避免不必要的SQL语句。任何有用的建议?谢谢!

编辑:在试图分析这个以了解时间在哪里,我使用Django Debug工具栏。它解释说,有超过200个查询,并且每个查询都运行得非常快,所以总的来说它们占用的时间很少。但是,SQL的执行速度是否相对较快,但是ORM的构建会增加,因为它发生了200次以上?我重构了一个需要花费3分钟才能加载的上一页,并使用values()而不是ORM,从而将页面加载到2.7秒和5条SQL语句。

+1

你或许应该配置文件,看是否放缓是由于SQL,或到Python代码。 – Oliver 2012-03-14 15:25:17

+0

刚刚编辑我的答案 - 我使用Django Debug工具栏进行分析,这意味着SQL调用都发生得相对较快,但很难看出时间在哪里。您使用哪些工具进行分析? – Nathan 2012-03-14 15:40:24

+0

您使用的数据库是? – 2012-03-14 17:30:13

回答

1

创建一个查询集不会触及数据库;只能从中访问结果。因此,仅仅创建查询集不是你的问题。

请注意,将查询集传递给另一个查询集不会创建两个查询。因此,构建命令不会减少数据库命中的数量。

如果您可以构建字典,那么您可能会设法创建比您更简单的查询,这会加快实际的查询执行速度。然而,这是一个单独的问题。

+0

权限 - 访问结果是命中数据库的结果。在这个实例中使用Django的ORM访问结果的自然方式似乎是构建超过200个不同的查询集。除了在ORM之外构建字典之外,是否有办法避免这种情况? – Nathan 2012-03-14 15:51:58

+0

@Nathan我重复一遍:构建查询集不会触及数据库。您必须访问查询集的内容。请注意,将查询集传递给另一个查询集不会创建两个查询。因此,构建命令不会减少数据库命中的数量。 – Marcin 2012-03-14 16:02:11

+1

我构建了200个不同的查询集并从每个查询集访问数据。我浏览了代码并观看了SQL日志,所以我知道你的意思。减少SQL语句数量的一种方法是通过使用'values()'而不是过滤来同时获取数据库的所有相关行,然后在Python中使用对象。 – Nathan 2012-03-14 16:08:42

1

这让我想起反向外键查找的情况。我们应该能够通过在版本中获取与PLM关联的所有pgi_models来减少循环的顶部。我假设你有一个PGI模型,PLM模型有一个名为pgi_model的外键字段。如果是这种情况,您可以通过以下方式在PLM版本中找到PGI。你仍然有一个循环,但循环迭代应减少,理论上:

pgis = PGI.objects.filter(plm__in=PLM.objects.filter(release=release)) 
for pgi in pgis: 
    peresults = plannedExecs.filter(plm=pgi.plm).count() 
    if peresults > 0: 
     try: 
      testSuite['pgi_model'].append((pgi['pgi_model'], "", "", peresults, int(peresults/float(testlistCount)*100))) 
     except ZeroDivisionError: 
      testSuite['pgi_model'].append((pgi['pgi_model'], "", "", peresults, 0))