记录 django 中的查询优化
恰当的使用 select_related
和 prefetch_related
方法,可以减少数据库重复查询的次数
两种方法均支持双下划线指定需要查询的关联对象的字段名
- select_related
- prefetch_related:
- 适合多对多字段、外键反查(
related_name
)的情况
- 在方法中使用 Prefetch 可以增加查询条件
- 执行两次数据库查询
示例 model
1 2 3 4 5
| class Article(models.Model): """文章模型""" title = models.CharField('标题', max_length=200, db_index=True) category = models.ForeignKey('Category', verbose_name='分类', on_delete=models.CASCADE, blank=False, null=False) tags = models.ManyToManyField('Tag', verbose_name='标签集合', blank=True)
|
适用于外键这种一对一,一对多的情况,执行其实是生成一条 inner join
的 SQL 语句,一次查询获取对象和关联对象的内容。
使用方法如下,遍历结果集 articles
调用 item.category.name
获取信息时,不产生另外的查询:
1 2 3 4 5 6 7 8 9 10
| articles = Article.objects.all().select_related('category') articles = Article.objects.all().select_related('category__name') articles = Article.objects.all().select_related('category', 'author__name')
articles = Article.objects.all().select_related()
articles = Article.objects.all().filter(pk__in=(1, 2, 3)).select_related()
|
在模板中遍历的示例,未使用 select_related
方式:
1 2 3 4 5 6 7 8 9 10 11 12
| <ul> {% for article in articles %} <li>{{ article.title }} </li> {# category.name 每次都将生成一条查询。若按上述方法,这里将从对象中直接拿到,不用额外的一次查询 #} <li>{{ article.category.name }}</li> <li> {% for tag in article.tags.all %} {{ tag.name }}, {# 每次都将生成一条查询 #} {% endfor %} </li> {% endfor %} </ul>
|
对于多对多的字段,不能使用 select_related
方式,避免多对多字段 join 后结果很大。prefect_related
就是用于多对多关系的,也可以用于外键的反查(related_name)
使用方法如下,遍历结果集,拿关联的 tags
对象信息时,不用每次遍历执行一次查询
1 2 3 4 5 6 7 8
| articles = Article.objects.all().prefetch_related('tags__name')
articles = Article.objects.all().prefetch_related('tags')
articles = Article.objects.all().select_related('category__name').prefetch_related('tags')
|
在 prefetch_related 中,还可以对所查的关联对象进行过滤
1 2 3 4 5 6 7 8 9 10
| Article.objects.all().prefetch_related( Prefetch('tags', queryset=Tag.objects.filter(name__startswith="P")) )
Article.objects.all().prefetch_related( Prefetch('tags', queryset=Tag.objects.filter(name__startswith="P")), to_attr='article_p_tag' )
|