python - 如何提高 Django,N + 1 问题中的查询性能?
问题描述
我在 Django 查询的性能方面遇到问题。假设我有 3 个模型,并且 Company 表中有 100 行:
from django.db import models
class Company(models.Model):
name = models.CharField()
def order_count(self):
return self.orders.count()
def order_sum(self):
return (self.orders.all().aggregate(models.Sum('total')))['total__sum']
class Customer(models.Model):
company = models.ForeignKey(Company, related_name="customer", on_delete=models.PROTECT)
name = models.CharField()
def order_count(self):
return self.orders.count()
class Order(models.Model):
company = models.ForeignKey(Company, related_name='orders')
customer = models.ForeignKey(Customer, related_name="orders")
value = models.FloatField()
我希望我的模板显示公司名称及其订单总和,然后对于该公司的每个客户,我想显示客户名称及其订单数量。我的查询代码views.py
使用这样的预取:
queryset = Company.objects.prefetch_related(
models.Prefetch('customer',
queryset=Customer.objects.prefetch_related('orders')), 'orders')
我的伪代码template
:
for company in queryset:
print(company.name, company.order_count, company.order_sum)
for customer in company:
print(customer.name, customer.order_count)
我检查了 Django 调试工具栏,它需要 105 个查询,使用这些 SQL 语句(伪代码):
SELECT * FROM company
SELECT * FROM customer WHERE customer.company_id IN (100 IDs of the companies)
SELECT * FROM order WHERE order.customer_id IN (the IDs from previous command)(this duplicates 2 times)
SELECT * FROM order WHERE order.company_id IN (100 IDs of the companies)
SELECT SUM(order.value) FROM order WHERE order.company_id = %s (this duplicates 100 times, for each company's id)
正如 Django 调试工具栏 (DjDT) 向我展示的那样:
- 当我评估查询集(模板中的 for 循环)时,前 5 个查询出现
- 当我请求 order_sum() (模板中的第 2 行)时,接下来的 100 个查询来了。有了这个,DjDT 告诉我它大约需要 700-800 毫秒(模板中的一些进程,但似乎花费的时间不多,我测试过)。我想将它减少到 500 毫秒。
所以我的问题是:
- 我可以做些什么来改进?
- 为什么第三个 SQL 查询重复 2 次。
- 有没有办法将最后一个 SQL 查询减少到只有 1 个查询?我是新手所以请帮助^^。
#非常感谢您的宝贵时间^^
解决方案
您可以使用 annotate 函数在单个查询中获取订单总和。例如
queryset = Company.objects.annotate(
order_sum=Sum("orders__value")
).prefetch_related(
models.Prefetch('customer', queryset=Customer.objects.prefetch_related('orders')), 'orders'
)
然后你可以像其他属性一样访问 order_sum 值,使用点运算符
for company in queryset:
print(company.order_sum)
您可以阅读Django 文档以获得更多理解
推荐阅读
- c# - 如何在 SQL Server 2008 中将空值添加到外键列?
- pandas - 从聚合函数制作数据框时无法指定列名
- mongodb - 如何在 MongoDB 文档中查找索引数组
- java - 调用另一个方法时调用一个方法?
- c++ - 从常量初始化 char 数组
- spring-boot - 使用 datasource-proxy 有没有办法不记录查询参数?
- python - AES 加密问题。无法用正确的密钥解密
- excel - 基于循环遍历具有 300 万行的 txt 文件中的值的 excel 匹配值的文本匹配 [慢]
- firebase - Firebase - 从数据库/firestore 数据执行分析
- windows - Windows(窗体)应用项目建议