【问题标题】:Django: How do I avoid unnecessary SQL statements?Django:如何避免不必要的 SQL 语句?
【发布时间】:2012-03-14 15:20:36
【问题描述】:

我正在优化我们(第一个)Django 项目中的缓慢页面加载。整个项目进行测试状态管理,因此有些协议具有已计划执行的案例。目前的代码是:

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 语句,这意味着仅这个 for 循环就有 100 条 SQL 语句。 (另外,似乎应该使用values_listflat=True。)

由于我想获得有关案例和计划执行的信息,我想我真的只需要检索这两个表,然后对其进行一些分析。在当时,使用 filter 和 count() 似乎是显而易见的解决方案,但我想知道我是否最好使用 .values() 构建相关案例和计划执行信息的字典,然后对其进行分析,所以以避免不必要的 SQL 语句。有什么有用的建议吗?谢谢!

编辑:在尝试对此进行分析以了解时间流逝时,我正在使用 Django Debug 工具栏。它解释说有 200 多个查询,每个查询都运行得非常快,因此总体上它们只占用很少的时间。但是,会不会是 SQL 的执行相对较快,但 ORM 的构建加起来,发生了 200 多次?我重构了一个需要 3 分钟加载的前一个页面,并使用 values() 代替 ORM,从而将页面加载时间缩短到 2.7 秒和 5 个 SQL 语句。

【问题讨论】:

  • 您可能应该对其进行分析以查看减速是由于 SQL 还是由于 python 代码。
  • 刚刚编辑了我的答案 - 我正在使用 Django Debug 工具栏进行分析,这意味着 SQL 调用都发生得相对较快,但在这种情况下很难看出时间在哪里。您使用什么工具进行分析?

标签: python sql django


【解决方案1】:

创建查询集不会命中数据库;只访问它的结果。因此,仅仅创建查询集不是您的问题。

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

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

【讨论】:

  • 正确 - 访问结果会影响数据库。在这种情况下,使用 Django 的 ORM 访问结果的自然方式似乎是构建 200 多个不同的查询集。除了在 ORM 之外构建字典之外,还有其他方法可以避免这种情况吗?
  • @Nathan 我重复一遍:构建查询集不会命中数据库。您必须访问查询集的内容。请注意,将查询集传递给另一个查询集不会创建两个查询。因此,构建 dicts 不会减少数据库命中次数。
  • 我构建了 200 个不同的查询集并从每个查询集访问数据。我已经浏览了代码并查看了 SQL 日志,所以我知道你的意思。减少 SQL 语句数量的一种方法是使用values() 而不是过滤一次获取数据库的所有相关行,然后在 Python 中处理对象。
  • @Nathan 是的,这是正确的。您可能想测试该方法并对其进行概要分析。但是,鉴于数据库已针对查询进行了优化,如果您必须大量遍历对象图,您可能会发现效率较低。
  • 好的。我以前做过,将页面加载时间从 3 分钟缩短到 3 秒,但它相当笨拙,我问了another question,他们建议可以使用模型管理器来做这些事情。
【解决方案2】:

这让我觉得是反向外键查找的一个案例。我们应该能够通过在发布中获取与 PLM 关联的所有 pgi_model 来减少顶部 for 循环。我假设您有一个 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))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 2011-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多