django 中的 Prefetch_related 和 select_related 函数
在 Django 中,select_related 和 prefetch_related 旨在阻止因访问相关对象而导致的数据库查询泛滥。在本文中,我们将看到它如何减少查询次数并使程序更快。
- select_related() “遵循”外键关系,在执行查询时选择其他相关对象数据。
- prefetch_related()对每个关系进行单独的查找,并在Python中进行“连接”。
当您要选择的对象是单个对象时,可以使用 select_related,因此 OneToOneField 或 ForeignKey。当您要获得一组“东西”时,您可以使用 prefetch_related ,因此您所说的 ManyToManyFields 或反转 ForeignKeys。只是为了澄清我所说的“反向外键”的意思。
举例说明 Prefetch_related 和 select_related 的概念——
上面的分类可能不太清楚让我们看一个例子:
class A(models.Model):
pass
class B(models.Model):
a = ForeignKey(ModelA)
# Forward ForeignKey relationship
A.objects.select_related('a').all()
# Reverse ForeignKey relationship
A.objects.prefetch_related('modelb_set').all()
select_related通过多表join关联查询一次性获取所有数据,通过减少数据库查询次数来提高性能。它使用SQL的JOIN语句,通过减少SQL查询次数来优化和提高性能。后者是通过JOIN语句解决SQL查询中的问题。但是对于多对多的关系,用SQL语句来解决是不明智的,因为JOIN得到的表会很长,会导致SQL语句的运行时间和内存占用增加。 prefetch_related() 的解决办法是分别查询每个表,然后用Python处理它们的关系!
这里有些例子 :
Models.py reads as follows:
from django.db import models
class Province(models.Model):
name = models.CharField(max_length = 10)
def __unicode__(self):
return self.name
class City(models.Model):
name = models.CharField(max_length = 5)
province = models.ForeignKey(Province)
def __unicode__(self):
return self.name
class Person(models.Model):
firstname = models.CharField(max_length = 10)
lastname = models.CharField(max_length = 10)
visitation = models.ManyToManyField(City, related_name = "visitor")
hometown = models.ForeignKey(City, related_name = "birth")
living = models.ForeignKey(City, related_name = "citizen")
def __unicode__(self):
return self.firstname + self.lastname
select_related –
我们使用 select_related()函数:
>>> citys = City.objects.select_related().all()
>>> for c in citys:
... print c.province
...
只有一个 SQL 查询,这显然大大减少了 SQL 查询的数量:
SELECT `Optimize_city`.`id`, `Optimize_city`.`name`,
`Optimize_city`.`province_id`, `Optimize_province`.`id`, `Optimize_province`.`name`
FROM`Optimize_city`
INNER JOIN `Optimize_province` ON
(`Optimize_city`.`province_id`=`Optimize_province`.`id`);
prefetch_related –
在这里我们可以看到 Django 使用了 INNER JOIN。我想澄清一件事,Optimize 是我们应用程序的名称。如果我们想获取湖北的所有城市名称,我们可以这样做:
> HB=Province.objects.prefetch_related('city_set').get(name__iexact=u"Hubei Province")
>>> for city in hb.city_set.all():
... city.name
...
触发的 SQL 查询:
SELECT `Optimize_province`.`id`, `Optimize_province`.`name`
FROM `Optimize_province`
WHERE `Optimize_province', `name `LIKE'Hubei Province';
SELECT `Optimize_city`.`id`, `Optimize_city`.`name`, `Optimize_city`.`province_id`
FROM `Optimize_city`
WHERE `Optimize_city`.`province_id` IN (1);
如我们所见,预取是使用 IN 语句实现的。这样,当QuerySet中的对象过多时,可能会出现性能问题,具体取决于数据库的特性。