Loading

django ORM 中 select_related 与 prefetch_related 的使用场景

在常规 models 操作中,某些对于关联表的查询稍有不慎可能导致产生多次查询,如:

# views.py
from . import models
def test(request):
    '''假设有一张user表,其关联表为`usertype`,关联字段为`ut`
    现在想要遍历user表每一条数据 
    并查询其关联字段`ut`所关联表的对应`title`字段
    '''
    # SELECT * FROM User
    userlist = models.User.objects.all()
    for row in userlist:
        # SELECT title FROM usertype WHERE id=row.id
        print('row.ut.title')

上面这种查询操作会造成不必要的多次查询,因为当 userlist = models.User.objects.all() 执行时,取出来的数据包含了 ut.id 但并不包含 ut.title,所以每次循环取值时都会去查询关联表的 title 字段。对此可以通过 select_related 方法进行优化,该方法会将关联表中的指定字段一次查询(即 JOIN 查询),从而避免产生多次查询:

def test(request):
    # SELECT * FROM User LEFT JOIN usertype on User.ut_id=usertype.id
    userlist = models.User.objects.all().select_related('ut')
    for row in userlist:
        ...

select_related 可以接收多个参数用于对多个关联表的连表查询。注意如果不需要对第三张表进行操作的话,则应该避免使用 select_related 方法,因为连表查询同样消耗时间。


使用 select_related 方法虽然能提升性能,但终究做了一次连表查询,在同等条件下,连表查询性能是低于单表查询的。所以 prefetch_related 就应运而生了。它会先执行子表的查询,然后通过计算得出结果关键外键的唯一值列表(可以理解为对所有关联外键进行 set 操作去除重复值),然后通过 IN 条件再从关联表中查询数据并放到内存中。示例如下:

models.UserInfo.objects.prefetch_related('ut')
# 先执行: SELECT * FROM UserInfo
# 通过计算获取到所有用户的用户类型ID(ut的所有唯一值) [1, 2, 3]
# 最后执行: SELECT * FROM usertype WHERE id IN (1, 2, 3)

参考

  1. QuerySet API reference | Django 文档 | Django
posted @ 2024-06-21 08:43  kingron  阅读(3)  评论(0编辑  收藏  举报