django ORM 中 select_related 与 prefetch_related 的使用场景
select_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
方法,因为连表查询同样消耗时间。
prefetch_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)