在数据库存在外键的其情况下,使用select_related()和prefetch_related()很大程度上减少对数据库的请求次数以提高性能

主要思想就是 ‘当我们在表中查找一个数据项的时候,我们 顺手 把连接到这个数据项的其他表中的关系数据项,一起查找出来,这样当我们对这个数据项进行操作的时候,就可以直接操作刚刚 顺手 拿出来的关系数据项,而不用再次去数据库中拿了’

1,两者适用范围

select_related() 适用于一对一OneToOneField 和 多对一(就是外键)ForeignKey
prefetch_related() 适用于 多对多ManyToManyField 和 一对多 (我自己称之为 反过来的外键,就是外键表里面的那个related_name里面的值)

class Pic(models.Model):xx = models.CharField(max_length=30)album = models.ForeignKey(to=Album, on_delete=models.CASCADE, related_name='pic')//如下面的prefetch_related('tags', 'pic') 中的‘pic'
album = Album.objects.prefetch_related('tags', 'pic').get(pk=id)//但是如果从Pic的角度上讲,连接album的话,就的用select_related()
pic = Pic.objects.select_related('album').get(pk=id)


以下是抄的:
https://www.cnblogs.com/mdzzbojo/p/9216122.html

Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化
引言

在数据库存在外键的其情况下,使用select_related()和prefetch_related()很大程度上减少对数据库的请求次数以提高性能

1.实例准备

模型:
复制代码

from django.db import models

Create your models here.

class Book(models.Model):
title = models.CharField(max_length=32)
publish_date = models.DateField(auto_now_add=True)
price = models.DecimalField(max_digits=5, decimal_places=2)
memo = models.TextField(null=True)
# 创建外键,关联publish
publisher = models.ForeignKey(to=“Publisher”, on_delete=models.CASCADE, related_name=‘books’)
# 创建多对多关联author
author = models.ManyToManyField(to=“Author”)

def __str__(self):return self.title

出版社

class Publisher(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)

def __str__(self):return self.name

作者

class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
phone = models.CharField(max_length=11)
detail = models.OneToOneField(to=“AuthorDetail”, on_delete=models.CASCADE)

def __str__(self):return self.name

作者详情

class AuthorDetail(models.Model):
addr = models.CharField(max_length=64)
email = models.EmailField()

复制代码

1.select_related()

对于一对一字段(OneToOneField)和外键字段(ForeignKey)可以使用select_releated来优化QuerySet查询

示例:

1)未调用该方法时,查询所有书籍以及所在的出版社

book_obj = models.Book.objects.all()

打印所有书籍以及所在的出版社

for book in book_obj:
print(book.publisher)

输出的日志信息
复制代码

(0.000) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.001) SELECT app_book.id, app_book.title, app_book.publish_date, app_book.price, app_book.memo, app_book.publisher_id FROM app_book; args=()
南方传媒出版社
长沙电视出版社
长沙电视出版社
清华大学出版社
人民邮电出版社
(0.001) SELECT VERSION(); args=None
南方传媒出版社
(0.001) SELECT app_publisher.id, app_publisher.name, app_publisher.city FROM app_publisher WHERE app_publisher.id = 1; args=(1,)
(0.001) SELECT app_publisher.id, app_publisher.name, app_publisher.city FROM app_publisher WHERE app_publisher.id = 2; args=(2,)
(0.000) SELECT app_publisher.id, app_publisher.name, app_publisher.city FROM app_publisher WHERE app_publisher.id = 2; args=(2,)
(0.000) SELECT app_publisher.id, app_publisher.name, app_publisher.city FROM app_publisher WHERE app_publisher.id = 4; args=(4,)
(0.001) SELECT app_publisher.id, app_publisher.name, app_publisher.city FROM app_publisher WHERE app_publisher.id = 3; args=(3,)
(0.000) SELECT app_publisher.id, app_publisher.name, app_publisher.city FROM app_publisher WHERE app_publisher.id = 1; args=(1,)

Process finished with exit code 0

复制代码

2)调用该方法时,查询所有书籍以及所在的出版社

book_obj = models.Book.objects.select_related().all()
for book in book_obj:
print(book.publisher)

输出的日志信息  
复制代码

(0.001) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.001) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.001) SELECT app_book.id, app_book.title, app_book.publish_date,
app_book.price, app_book.memo, app_book.publisher_id, app_publisher.id,
app_publisher.name, app_publisher.city FROM app_book INNER JOIN app_publisher
ON (app_book.publisher_id = app_publisher.id); args=()
南方传媒出版社
长沙电视出版社
长沙电视出版社
清华大学出版社
人民邮电出版社
南方传媒出版社

Process finished with exit code 0

复制代码

结论:

在对QuerySet使用select_related()函数前,会出现线性的SQL查询,如果存在n个查询对象,每个对象存在k个外键字段时,就会出现n*k + 1 次SQL查询

在对QuerySet使用select_related()函数后,Django会在进行第一次查询时获取相应外键对应的对象,从而在之后需要的时候不必再查询数据库,只需要进行一次查询

3)相关参数

*fields参数,select_related() 接受可变长参数,每个参数是需要获取的外键(父表的内容)的字段名,以及外键的外键的字段名、外键的外键的外键等,若要选择外键的外键需要使用两个下划线“__”来连接

示例:通过外键获取书籍的出版社的详细地址

obj = models.Book.objects.select_related(‘publisher__detail’).get(title=‘武林传奇’)
print(obj.publisher.detail)
print(obj.publisher)

输出的日志信息    
复制代码

(0.000) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT VERSION(); args=None
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.001) SELECT app_book.id, app_book.title, app_book.publish_date, app_book.price, app_book.memo, app_book.publisher_id, app_publisher.id, app_publisher.name, app_publisher.city, app_publisher.detail_id, app_publisherdetail.id, app_publisherdetail.addr, app_publisherdetail.create_date FROM app_book INNER JOIN app_publisher ON (app_book.publisher_id = app_publisher.id) INNER JOIN app_publisherdetail ON (app_publisher.detail_id = app_publisherdetail.id) WHERE app_book.title = ‘武林传奇’; args=(‘武林传奇’,)
长沙南区
长沙电视出版社
复制代码

结论:

Django使用了2次 INNER JOIN 来完成请求,获得了publisher表和publisherdetail表的内容并添加到结果表的相应列,再次调用这两个对象的时候也不必再次进行SQL查询;反之如是未指定的外键则会进行SQL查询,且深度更深,查询次数更多

depth参数,select_related() 接受depth参数,depth参数可以确定select_related的深度,Django会递归遍历指定深度内的所有的OneToOneField和ForeignKey

无参数,select_related() 也可以不加参数,这样表示要求Django尽可能深的select_related

总结:
复制代码

1.select_related主要针一对一和多对一关系进行优化
2.select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能
3.可以通过可变长参数指定需要select_related的字段名。也可以通过使用双下划线“__”连接字段名来实现指定的递归查询。没有指定的字段不会缓存,没有指定的深度不会缓存,如果要访问的话Django会再次进行SQL查询
4.也可以通过depth参数指定递归的深度,Django会自动缓存指定深度内所有的字段。如果要访问指定深度外的字段,Django会再次进行SQL查询
5.也接受无参数的调用,Django会尽可能深的递归查询所有的字段。但注意有Django递归的限制和性能的浪费
6.Django >= 1.7,链式调用的select_related相当于使用可变长参数。Django < 1.7,链式调用会导致前边的select_related失效,只保留最后一个

复制代码

2.prefetch_releated()

对于多对多字段(ManyToManyField)和一对多(ForeignKey)字段,可使用prefetch_related()来进行优化,它和select_releated的设计目的相似,都是为了减少SQL的查询次数,但实现方式不一样,后者是通过JOIN语句在SQL查询内解决问题。但由于JOIN语句过于冗长,不适合解决多对多关系,会导致SQL语句运行时间的增加和内存占用增加

prefetch_releated的解决方法是分别查询每个表,然后用Python处理他们之间的关系

示例:

book_obj = models.Book.objects.prefetch_related(‘publisher’).get(title=‘七侠五义’)
print(book_obj.publisher.detail)

输出的日志信息
复制代码

(0.000) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.001) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT VERSION(); args=None
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT app_book.id, app_book.title, app_book.publish_date, app_book.price, app_book.memo, app_book.publisher_id FROM app_book WHERE app_book.title = ‘七侠五义’; args=(‘七侠五义’,)
(0.000) SELECT app_publisher.id, app_publisher.name, app_publisher.city, app_publisher.detail_id FROM app_publisher WHERE app_publisher.id IN (1); args=(1,)
(0.001) SELECT app_publisherdetail.id, app_publisherdetail.addr, app_publisherdetail.create_date FROM app_publisherdetail WHERE app_publisherdetail.id = 1; args=(1,)
广州天河区

Process finished with exit code 0

复制代码

结论:

可以看出,第一条查询语句拿到的要操作的对象,关键在于第二条拿到了要查询结果的条件也就是ID,第三行通过第二行拿到的ID进行结果查询

从第二条查询语句中可看出,可以看见,prefetch使用的是 IN 语句实现的。这样,在QuerySet中的对象数量过多的时候,根据数据库特性的不同有可能造成性能问题

*lookups参数,prefetch_related()在Django < 1.7 只有这一种用法。和select_related()一样,prefetch_related()也支持深度查询

要注意的是,在使用QuerySet的时候,一旦在链式操作中改变了数据库请求,之前用prefetch_related缓存的数据将会被忽略掉。这会导致Django重新请求数据库来获得相应的数据,从而造成性能问题。这里提到的改变数据库请求指各种filter()、exclude()等等最终会改变SQL代码的操作。而all()并不会改变最终的数据库请求,因此是不会导致重新请求数据库的

总结:
复制代码

1.prefetch_related主要针一对多和多对多关系进行优化
2.prefetch_related通过分别获取各个表的内容,然后用Python处理他们之间的关系来进行优化
3.可以通过可变长参数指定需要select_related的字段名。指定方式和特征与select_related是相同的
4.在Django >= 1.7可以通过Prefetch对象来实现复杂查询,但低版本的Django好像只能自己实现
5.作为prefetch_related的参数,Prefetch对象和字符串可以混用
6.prefetch_related的链式调用会将对应的prefetch添加进去,而非替换,似乎没有基于不同版本上区别
7.可以通过传入None来清空之前的prefetch_related

复制代码

归纳:

1.因为select_related()总是在单次SQL查询中解决问题,而prefetch_related()会对每个相关表进行SQL查询,因此select_related()的效率通常比后者高
2.鉴于第一条,尽可能的用select_related()解决问题。只有在select_related()不能解决问题的时候再去想prefetch_related()
3.可以在一个QuerySet中同时使用select_related()和prefetch_related(),从而减少SQL查询的次数
4.只有prefetch_related()之前的select_related()是有效的,之后的将会被无视掉

Django ORM相关操作 select_related和prefetch_related函数对 QuerySet 查询的优化相关推荐

  1. Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(二)

    3. prefetch_related() 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化.或许你会说,没有一个叫OneToMan ...

  2. 实例具体解释Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(二)...

    这是本系列的第二篇,内容是 prefetch_related() 函数的用途.实现途径.以及用法. 本系列的第一篇在这里 第三篇在这里 3. prefetch_related() 对于多对多字段(Ma ...

  3. Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(三)

    4.一些实例 如果我们想要获得所有家乡是湖北的人,最无脑的做法是先获得湖北省,再获得湖北的所有城市,最后获得故乡是这个城市的人.就像这样: 1 2 3 4 5 >>> hb = Pr ...

  4. Python-Django框架的select_related 和 prefetch_related函数对 QuerySet 查询的优化

    概念: select_related()当执行它的查询时它沿着外键关系查询关联的对象数据.它会生成一个复杂的查询并引起性能的消耗,但是在以后使用外键关系时将不需要数据库查询. prefetch_rel ...

  5. python---django中orm的使用(5)数据库的基本操作(性能相关:select_related,和prefetch_related重点)(以及事务操作)...

    ################################################################## # PUBLIC METHODS THAT ALTER ATTRI ...

  6. django ORM相关的那些操作汇总

    必知必会13条 <1> all(): 查询所有结果<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象<3> get(**kwargs): ...

  7. Django ORM 数据库操作

    比较有用 转自 http://blog.csdn.net/fgf00/article/details/53678205 一.DjangoORM 创建基本类型及生成数据库表结构 1.简介 2.创建数据库 ...

  8. Python之数据分析(Numpy的矩阵相关操作、ufunc泛化函数对象)

    文章目录 一.矩阵相关操作 二.ufunc统一泛化函数 一.矩阵相关操作 1.三种构造矩阵的方法 np.matrix(二维容器, copy=True) 一参为可被解释为矩阵的二维容器,比如二维数组.二 ...

  9. Django 数据库相关操作 (六)

    前言 上篇已经介绍过模型相关操作,并创建好了数据库及相关表字段,接下来将通过以下表在Django中进行表数据的增删改查. from django.db import modelsclass Stude ...

最新文章

  1. 莱比特矿池CEO江卓尔:BCH作为货币不需要新功能,但出于货币竞争的考虑需要
  2. python bytes()函数
  3. linux 使用 cp 命令强制覆盖功能
  4. capwap学习笔记——capwap的前世今生(转)
  5. C#使用Xamarin开发可移植移动应用进阶篇(10.综合演练,来一份增删改查CRUD)
  6. 交换机的基本配置实验报告_无线网络设计配置即实验报告
  7. Sublime Text 3 无法输入中文解决方案
  8. Python查找所有类似于123-45-67+89 = 100的组合
  9. 关于在下次升级中向Cosmos Hub添加流动性模块的提案发布
  10. 零基础带你学习MySQL—Insert语句以及注意事项(七)
  11. spring注解记录
  12. Application.messagebox对话框样式
  13. SCPPO(十二):SQL误操作如何恢复?
  14. IIC总线协议详解与总结
  15. three.js学习笔记(十四)——Shaders着色器
  16. 新的 systemctl 命令代替原来的 chkconfig、service 命令
  17. 信息技术助推智能建造和建筑企业数字化转型
  18. 阿里api文档链接地址
  19. 小米note2不上Android9吗,我的第二部小米手机,小米9简单到不能再简单的简单体会...
  20. 计算机网络9 无线网络和移动网络

热门文章

  1. 【数学知识】角度与弧度
  2. 实体框架(Entity Framework)--简介
  3. 镌刻爱之誓言 联想YOGA情人节专属定制
  4. 华为路由器:loopback(本地回环)接口的作用
  5. 扫码登录实现原理,附代码
  6. 如何用公式编辑器打傅立叶变换符号
  7. Linux下将文件夹压缩成.zip命令
  8. Google的非主流功能
  9. linux下查看usb插拔日志,Linux下侦测 USB Disk 插拔
  10. 自己用C语言写itoa 函数和atoi函数