MST

星途 面试题库

面试题:Python Django ORM多表复杂关联查询及性能调优

假设有三个模型:用户(User)、订单(Order)和产品(Product)。用户可以有多个订单,每个订单可以包含多个产品。现在要查询购买了特定产品且用户注册时间在某一时间段内的所有订单信息,包括订单号、购买产品名称及用户姓名。请用Django的ORM写出实现此查询的代码,并分析可能存在的性能问题及如何优化。
29.9万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试
  1. Django ORM查询代码
from django.db.models import Q
from your_app.models import User, Order, Product

# 假设特定产品名称为'specific_product_name',注册时间范围为start_date到end_date
specific_product_name ='specific_product_name'
start_date = '2023-01-01'
end_date = '2023-12-31'

orders = Order.objects.filter(
    products__name=specific_product_name,
    user__registration_date__range=[start_date, end_date]
).values(
    'order_number',
    'products__name',
    'user__name'
)
  1. 可能存在的性能问题
    • 多表连接开销:此查询涉及UserOrderProduct三个表的连接。在关系型数据库中,多表连接操作通常会带来一定的性能开销,尤其是当表数据量较大时。
    • 数据库扫描filter操作可能会导致数据库全表扫描,如果没有合适的索引,查询性能会显著下降。例如,在Product表的name字段、User表的registration_date字段以及关联表中的外键字段上没有索引,数据库在执行查询时需要遍历整个表来匹配条件。
  2. 优化方法
    • 添加索引
      • Product模型的name字段上添加索引:
class Product(models.Model):
    name = models.CharField(max_length=100, db_index=True)
    # 其他字段...
 - 在`User`模型的`registration_date`字段上添加索引:
class User(models.Model):
    registration_date = models.DateField(db_index=True)
    # 其他字段...
 - 确保`Order`模型与`User`和`Product`模型关联的外键字段也有索引(Django通常会自动为外键字段添加索引,但可以再次确认)。
  • 使用select_relatedprefetch_related:如果后续需要访问Order对象的关联UserProduct对象的更多详细信息,可以使用select_related(适用于一对一和外键关系)或prefetch_related(适用于多对多和反向一对多关系)来减少数据库查询次数。例如:
orders = Order.objects.select_related('user', 'products').filter(
    products__name=specific_product_name,
    user__registration_date__range=[start_date, end_date]
).values(
    'order_number',
    'products__name',
    'user__name'
)

select_related通过SQL的JOIN操作一次性获取关联对象的数据,而prefetch_related通过分别执行查询然后在Python层面合并结果,适用于复杂的多对多关系。具体选择哪种方法取决于实际的数据量和查询复杂度。