Django笔记十一之外键查询优化select_related和prefetch_related
本篇笔记目录如下:
- select_related
- prefetch_related
在介绍 select_related 和 prefetch_related 这两个函数前,我们先来看一个例子。
对于,Entry 和 Blog 这两个 model,前面介绍过,Blog 是 Entry 的外键,如下:
class Blog(models.Model):name = models.CharField(max_length=100)tagline = models.TextField()class Entry(models.Model):blog = models.ForeignKey(Blog, on_delete=models.CASCADE)headline = models.CharField(max_length=255)body_text = models.TextField()pub_date = models.DateField()mod_date = models.DateField()authors = models.ManyToManyField(Author)number_of_comments = models.IntegerField()number_of_pingbacks = models.IntegerField()rating = models.IntegerField()
比如我们需要获取 Entry 的前十条数据,然后打印出关联的 Blog 的 name 字段信息。
我们一般会如此操作:
for entry in Entry.objects.all()[:10]if entry.blog:print(entry.blog.name)else:print("没有关联 blog 数据")
但是这样会有一个问题,那就是,这个 for 循环的操作会查询数据十一次,一次查询 Entry 数据,十次是查询每个 entry_obj 关联的 blog 数据。
这个设计对于系统来说是不合理的,想一想如果我们查询的数据是一千条,一万条,无论是系统接口的等待时间,还是数据库的访问压力,都是不可接受的。
因此我们可以引入 外键 和 ManyToManyTo 的一种能够减少数据库的访问次数的方式:select_related,prefetch_related。
1、select_related
当我们在使用的时候,如果有需要获取的外键数据,比如 Entry 关联的 Blog 数据,则可以将其字段名作为参数传入,这样在获取数据的时候就可以一次性将所有关联的 Blog 数据也取出来,而不用单独再去查询一遍数据库。
如下,批量操作:
for entry in Entry.objects.select_related("blog").all():print(e.blog) # 这个操作不会额外再去查询数据库
当然也适用于单条数据:
e = Entry.objects.get(id=5).select_related("blog")
为了验证 select_related() 确实会只查询一遍数据库,有两种方法:
一种是在数据库层面打印出来所有查询的 SQL语句,
另一种可以从侧面表示,那就是在系统层面打印出我们的查询条件转化的 SQL 语句。
比如:
Entry.objects.select_related("blog").all().query.__str__()
可以看到会输出一个 关联了 Blog 表的 inner join 的 SQL 语句。
SELECT `blog_entry`.`id`, `blog_entry`.`blog_id`, `blog_entry`.`headline`, `blog_entry`.`body_text`, `blog_entry`.`pub_date`, `blog_entry`.`mod_date`, `blog_entry`.`number_of_comments`, `blog_entry`.`number_of_pingbacks`, `blog_entry`.`rating`, `blog_blog`.`id`, `blog_blog`.`name`, `blog_blog`.`tagline` FROM `blog_entry` INNER JOIN `blog_blog` ON (`blog_entry`.`blog_id` = `blog_blog`.`id`)
链式获取外键数据
比如下面的 model:
class City(models.Model):passclass Person(models.Model):hometown = models.ForeignKey(City, on_delete=models.SET_NULL, blank=True, null=True)class Book(models.Model):author = models.ForeignKey(Person, on_delete=models.CASCADE)
我们可以通过以下语句来将 Book 关联的 Person,以及该条 Person 数据关联的 City 数据一起查询出来:
book = Book.objects.select_related("author__hometown").get(id=4)
person = book.author
city = person.hometown
因为我们在第一步查询的时候,通过双下划线将两个外键字段连接在一起取了出来,所以在第二步和第三步取 Person 数据和 City 数据的时候,就不需要再次查询数据库了。
同时获取多个外键关联字段
如果一个 model 有两个外键字段 foo 和 bar,那么下面的两种写法都将这两个外键字段关联取出:
select_related("foo", "bar")
select_related("foo").select_related("bar")
需要注意的是,这个链式的操作和 order_by() 的结果是不一样的哦,前面提到的 order_by() 的链式操作会导致后面的覆盖前面的,但是取外键数据的时候会同时取出。
注意: select_related() 仅作用于 ForeignKey 和 OneToOne,如果是 ManyToMany 字段,则需要用到下面的 prefetch_related() 函数。
2、prefetch_related()
prefetch_related() 和 select_related() 作用类似,都是通过减少查询的次数,来实现查询优化。
但 prefetch_related() 是针对 ManyToMany 的操作。
举个例子:
from django.db import modelsclass Topping(models.Model):name = models.CharField(max_length=30)class Pizza(models.Model):name = models.CharField(max_length=50)toppings = models.ManyToManyField(Topping)def __str__(self):return "%s (%s)" % (self.name,", ".join(topping.name for topping in self.toppings.all()),)
当我们执行:
Pizza.objects.all()
的时候,因为每一条 Pizza 数据实例化的时候,都会调用 str() 函数,而这个函数会再次去请求一遍数据库,所以多条 Pizza 数据会导致查询多次数据库。
因为我们可以使用 prefetch_related() 函数来达到减少查询的目的:
Pizza.objects.prefetch_related('toppings').all()
这样的话,对数据库的查询会减少到两次,一次是查询出所有的 Pizza 数据,一次是根据所有的 pizza_id 找到所有关联的 topping 数据。
如果有兴趣,可以比对下面两条语句在 shell 中执行的时候,MySQL 服务器接收到的 SQL 查询语句:
Pizza.objects.all()Pizza.objects.prefetch_related('toppings').all()
下面一种情况需要注意哦:
pizzas = Pizza.objects.prefetch_related('toppings')
[list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
因为第二步操作里,会对 toppings 数据进行一次新的 filter 过滤操作,所以会导致每次该语句重新去查询数据库,也就是说,我们的 prefetch_related() 操作是失效的。
以上就是本篇笔记全部内容,接下来会介绍查询里的 defer 和 only 函数。
本文首发于本人微信公众号,可搜索关注:Django笔记。
如果想获取更多相关文章,可扫码关注阅读:
Django笔记十一之外键查询优化select_related和prefetch_related相关推荐
- Django笔记六之外键ForeignKey介绍
这一篇笔记介绍 Django 系统 model 的外键处理,ForeignKey 以及相应的处理方法. 这是一种一对多的字段类型,表示两张表之间的关联关系. 本篇笔记的目录如下: on_delete ...
- Django ORM查询之外键、关系的反向引用
关系本身就是相互的,只用在一个表中记录,而不是在有关系的两个表中都记录.所以外键.关系提供反向引用机制.当然,外键可以是多个表的外键,关系也可以与多个表有关系,所以反向引用必须显式指出关系对方表(然后 ...
- Django笔记二十八之数据库查询优化汇总
这一篇笔记将从以下几个方面来介绍 Django 在查询过程中的一些优化操作,有一些是介绍如何获取 Django 查询转化的 sql 语句,有一些是理解 QuerySet 是如何获取数据的. 以下是本篇 ...
- Django笔记总结
1.web框架的本质 web通信流程 web我们这里指的就是通过浏览器去访问服务端,请求页面或者数据的通信方式,属于B/S架构.就是我们常见的网站.浏览器与服务端的通信流程:浏览器客户端发送一个请求信 ...
- Django笔记七之ManyToMany和OneToOne介绍
ManyToMany 是一种多对多的关系,在用途和使用方法上和外键 ForeignKey 类似. 以下是本篇笔记的目录: ManyToMany 的介绍 through 参数 through_field ...
- Django笔记十二之defer和only
本篇笔记将介绍查询中的 defer 和 only 两个函数的用法,笔记目录如下: defer only 1.defer defer 的英语单词的意思是 延迟.推迟,我们可以通过将字段作为参数传入,可以 ...
- Django 笔记4 -- 模板
Django 笔记4 – 模板 Django 系列笔记是笔者学习.实践使用 Django 的相关笔记,大量参考了知了课堂的<Django零基础到项目实战>教程. 参考文档: Django官 ...
- 家族关系查询系统程序设计算法思路_【学习笔记】数据库基础 - 查询优化
目录 什么是数据库查询优化? 影响查询优化的因素 优化策略概述 查询优化的总体思路 语义优化 -- 内容等价性 语法优化(逻辑层优化)---语法等价性 执行优化(物理层优化) 查询优化在DBMS中的位 ...
- Django笔记-5-视图
2019独角兽企业重金招聘Python工程师标准>>> Django笔记-5-视图 概述 作用 视图接受web请求, 并相应web请求; 本质 视图就是一个Python中的函数 相应 ...
- Django笔记:ORM模型
Django中操作数据库的方式有两种,一种是使用ORM模型,另一种是直接执行SQL,推荐使用ORM模型的方式来管理数据库,因为当需要执行的数据库操作过多时,第二种方式产生的SQL会非常难于管理和维护, ...
最新文章
- linux内核 RCU机制概述
- php codeigniter ext,php – 私有服务器上CodeIgniter不正确的系统路径
- 程序员面试金典 - 面试题 16.26. 计算器(栈)
- gridview使用经验
- 关于 JQuery 的克隆
- Python 扩展知识:编程习惯
- eclipse修改jsp不生效_JSP+Servlet+JDBC+mysql实现的个人日记本系统
- 对天敏电视精灵I老版播放器的修改和分析
- Dialog去掉默认白色背景
- 聚合数据API用法简介
- VM虚拟机下如何和Windows主机共享文件夹
- rap格式鸿蒙,你,想要成为rap star吗?
- PDF文件拆分合并器PDF Merge PDF Splitter + Mac
- adb 通过 无线连接手机
- 获得淘宝app商品详情原数据API
- laravel中seed
- stm32 spi nss硬件模式配置参考程序
- 整车控制器软件功能检测工装
- Unkown host ‘raw.githubusercontent.com‘以及超时问题
- 2021-04-08 使用Eclipse进行Web前端开发
热门文章
- 日常办公,无特殊要求者怎么选择显示器?
- 计算机基础知识面试题集合(包含计网OSI、TCP/IP、HTTP、TCP、UDP、三次握手、四次挥手、OS进程线程、死锁,常见数据结构及排序,Linux常用命令、数据库基础等。)
- Android手机玩8086汇编
- 产品经理的素质能力模型
- 地坛——我的最爱 (2006-11-12 09:33:18)
- fiddler手机抓包问题详解
- 计算机网络和internet选项,详细教你电脑ie的internet选项在哪
- 80004005错误代码_0x80004005,详细教您解决0x80004005错误代码的方法
- 转载:技术大停滞——范式春梦中的地球工业文明:前言
- 大物实验数据处理——用C求标准误差、标准偏差、标准偏差、相对误差