概念:
  • select_related()当执行它的查询时它沿着外键关系查询关联的对象数据。它会生成一个复杂的查询并引起性能的消耗,但是在以后使用外键关系时将不需要数据库查询。
  • prefetch_related()返回的也是QuerySet,它将在单个批处理中自动检索每个指定查找的对象。这具有与select_related类似的目的,两者都被设计为阻止由访问相关对象而导致的数据库查询的泛滥,但是策略是完全不同的。
  • select_related通过创建SQL连接并在SELECT语句中包括相关对象的字段来工作。因此,select_related在同一数据库查询中获取相关对象。然而,为了避免由于跨越“多个'关系而导致的大得多的结果集,select_related限于单值关系 -外键一对一关系
  • prefetch_related,另一方面,为每个关系单独查找,并在Python中“加入”。这允许它预取多对多多对一对象,除了外键一对一关系,它们不能使用select_related来完成
按例如下:
  • 首先我们在setting.py文件里设置SQL查询语句:
LOGGING = {'version': 1,'disable_existing_loggers': False,'handlers': {'console':{'level':'DEBUG','class':'logging.StreamHandler',},},'loggers': {'django.db.backends': {'handlers': ['console'],'propagate': True,'level':'DEBUG',},}
}
创建一个model.py
from django.db import modelsclass Province(models.Model):"""省"""name = models.CharField(max_length=10)def __unicode__(self):return self.nameclass City(models.Model):"""市"""name = models.CharField(max_length=5)province = models.ForeignKey(Province)def __unicode__(self):return self.nameclass 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()
  • 对于一对一字段(OneToOneField)外键字段(ForeignKey),可以使用select_related 来对QuerySet进行优化。
  • 接下来我们看案例:
>>> City.objects.all()
#  打印结果是:有四条数据 <QuerySet [<City: Hcity>, <City: Ccity>, <City: Ecity>, <City: Qcity>]># 下面我们来实现外键字段的操作:
>>> citys = City.objects.all()
>>> for city in citys:
...     print city.province
"""
BeiJing
BeiJing
BeiJing
BeiJing
"""
  • 下面我们打开SQL查询的语句:(可以看到查询了5次数据库)
(0.000) SELECT `apps_city`.`id`, `apps_city`.`name`, `apps_city`.`province_id` FROM `apps_city`; args=()
(0.000) SELECT `apps_province`.`id`, `apps_province`.`name` FROM `apps_province` WHERE `apps_province`.`id` = 2; args=(2,)
BeiJing
(0.000) SELECT `apps_province`.`id`, `apps_province`.`name` FROM `apps_province` WHERE `apps_province`.`id` = 2; args=(2,)
BeiJing
(0.000) SELECT `apps_province`.`id`, `apps_province`.`name` FROM `apps_province` WHERE `apps_province`.`id` = 2; args=(2,)
BeiJing
(0.000) SELECT `apps_province`.`id`, `apps_province`.`name` FROM `apps_province` WHERE `apps_province`.`id` = 2; args=(2,)
BeiJing
  • 下面我们用select_related()来查看一下查询的次数
>>> citys = City.objects.select_related().all()
>>> for city in citys:
...     print city.province
# 看打印结果:同样返回的4条数据
"""
BeiJing
BeiJing
BeiJing
BeiJing
"""
  • 接下来我们看SQL语句的查询:(只有一条,仔细观察INNER JOIN想起了连表查询吧)
    显然大大的减少了SQL查询的次数
(0.000) SELECT `apps_city`.`id`, `apps_city`.`name`, `apps_city`.`province_id`, `apps_province`.`id`, `apps_province`.`name` FROM `apps_city` INNER JOIN `apps_province` ON (`apps_ci
ty`.`province_id` = `apps_province`.`id`); args=()
接下来我们给select_related() 添加:*fields 参数
  • select_related() 接受可变长参数,每个参数是需要获取的外键(父表的内容)的字段名,以及外键的外键的字段名外键的外键的外键…。若要选择外键的外键需要使用两个下划线“__”来连接。
  • 我们举个例子:
>>> persons = Person.objects.select_related("living__province").get(pk=1)
>>> persons.living.province
# 输出结果: <Province: BeiJing>
  • 接下来看出发的SQL语句:
(0.000) SELECT `apps_person`.`id`, `apps_person`.`firstname`, `apps_person`.`lastname`, `apps_person`.`hometown_id`, `apps_person`.`living_id`, `apps_city`.`id`, `apps_city`.`name`,`apps_city`.`province_id`, `apps_province`.`id`, `apps_province`.`name` FROM `apps_person` INNER JOIN `apps_city` ON (`apps_person`.`living_id` = `apps_city`.`id`) INNER JOIN `apps
_province` ON (`apps_city`.`province_id` = `apps_province`.`id`) WHERE `apps_person`.`id` = 1; args=(1,)
  • 以上可以看出来,Django使用了2次INNER JOIN来完成请求,取到了city表和province表的内容,并添加到结果表的相应列,在调用查询persons.living的时候也不必再次进行SQL查询。
  • 如果未指定外键则不会被添加到结果中。
>>> persons.hometown.province
(0.001) SELECT `apps_city`.`id`, `apps_city`.`name`, `apps_city`.`province_id` FROM `apps_city` WHERE `apps_city`.`id` = 1; args=(1,)
(0.000) SELECT `apps_province`.`id`, `apps_province`.`name` FROM `apps_province` WHERE `apps_province`.`id` = 2; args=(2,)
<Province: BeiJing>
  • 同时,如果不指定外键,就会进行两次查询。如果深度更深,查询的次数就越多。
  • Diango1.7开始,select_related()函数的作用方式改变了。在1.7版本以前select_related()只能这么做:
>>> persons = Person.objects.select_related("hometown__province","living__province").get(pk=1)
  • 看SQL执行的查询:

(0.003) SELECT `apps_person`.`id`, `apps_person`.`firstname`, `apps_person`.`lastname`, `apps_person`.`hometown_id`, `apps_person`.`living_id`, `apps_city`.`id`, `apps_city`.`name`,`apps_city`.`province_id`, `apps_province`.`id`, `apps_province`.`name`, T4.`id`, T4.`name`, T4.`province_id`, T5.`id`, T5.`name` FROM `apps_person` INNER JOIN `apps_city` ON (`app
s_person`.`hometown_id` = `apps_city`.`id`) INNER JOIN `apps_province` ON (`apps_city`.`province_id` = `apps_province`.`id`) INNER JOIN `apps_city` T4 ON (`apps_person`.`living_id`
= T4.`id`) INNER JOIN `apps_province` T5 ON (T4.`province_id` = T5.`id`) WHERE `apps_person`.`id` = 1; args=(1,)
# 从下面我们可以看出来,通过外键获取到的数据,不会进行数据库的查询:
>>> persons.living.province
<Province: BeiJing>
>>> persons.hometown.province
<Province: BeiJing>
  • 但是在1.7以上版本,可以像QuerySet的其他函数一样进行操作:
>>> persons = Person.objects.select_related("living__province").select_related("hometown__province").get(pk=1)
  • 看SQL语句的查询:
(0.001) SELECT `apps_person`.`id`, `apps_person`.`firstname`, `apps_person`.`lastname`, `apps_person`.`hometown_id`, `apps_person`.`living_id`, `apps_city`.`id`, `apps_city`.`name`,`apps_city`.`province_id`, `apps_province`.`id`, `apps_province`.`name`, T4.`id`, T4.`name`, T4.`province_id`, T5.`id`, T5.`name` FROM `apps_person` INNER JOIN `apps_city` ON (`app
s_person`.`hometown_id` = `apps_city`.`id`) INNER JOIN `apps_province` ON (`apps_city`.`province_id` = `apps_province`.`id`) INNER JOIN `apps_city` T4 ON (`apps_person`.`living_id`
= T4.`id`) INNER JOIN `apps_province` T5 ON (T4.`province_id` = T5.`id`) WHERE `apps_person`.`id` = 1; args=(1,)
>>> persons.living.province
<Province: BeiJing>
>>> persons.hometown.province
<Province: BeiJing>
>>>
  • 建议大家使用1.7以上版本
二:prefetch_related()
  • 下面我来们操作一下:
>>> persons = Person.objects.prefetch_related("visitation__province").get(pk=1)
  • 查询SQL语句:
(0.007) SELECT (`apps_person_visitation`.`person_id`) AS `_prefetch_related_val_person_id`, `apps_city`.`id`, `apps_city`.`name`, `apps_city`.`province_id` FROM `apps_city` INNER JO
IN `apps_person_visitation` ON (`apps_city`.`id` = `apps_person_visitation`.`city_id`) WHERE `apps_person_visitation`.`person_id` IN (1); args=(1,)
>>> persons.visitation
<django.db.models.fields.related_descriptors.ManyRelatedManager object at 0x0000000003FCAAC8>
  • prefetch_related()的操作和 select_related()大概是相同的, 只是prefetch_related()是操作,多对多的表格。

Python-Django框架的select_related 和 prefetch_related函数对 QuerySet 查询的优化相关推荐

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

    在数据库存在外键的其情况下,使用select_related()和prefetch_related()很大程度上减少对数据库的请求次数以提高性能 主要思想就是 '当我们在表中查找一个数据项的时候,我们 ...

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

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

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

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

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

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

  5. liunx+python+django框架实现图片生成二维码

    点击箭头处"蓝色字",免费领测试技术及面试资料! 余生皆欢喜 liunx+python+django框架实现二维码生成器 首先我简单的介绍下django:django官网解释就是如 ...

  6. 利用 Python django 框架 输入汉字,数字,字符,等。。转成二维码!

    利用 Python django 框架 输入汉字,数字,字符,等..转成二维码! 模块必备:Python环境 + pillow  + qrcode 模块 核心代码import qrcodeqr = q ...

  7. Python+Django框架Web应用开发-欧阳桫-专题视频课程

    Python+Django框架Web应用开发-237人已学习 课程介绍         系统介绍使用Django框架开发Web应用程序,当然,依然水煮风格 课程收益     掌握Django框架的使用 ...

  8. Python Django框架入门第一课

    Python Django框架入门 1.Django简介 Django使用python开发的一个免费的Web框架,几乎囊括了Web应用的方方面面,用于快速搭建高性能.优雅的网站. 2.配Django运 ...

  9. python网站设计理念_简单介绍下python Django框架的历史,设计理念及优势_Django讲解2...

    简单介绍下python Django框架的历史,设计理念及优势 Django是一个高层次的 Python Web 框架,它是一个鼓励快速开发和干净,实用的框架设计.Django可以更容易地快速构建更好 ...

最新文章

  1. url指定服务器,Linux服务器设置定时任务来访问指定url
  2. 赴马来西亚旅游遇车祸 70岁中国籍老人不幸身亡
  3. 10丨 Redis主从同步与故障切换,有哪些坑
  4. python3 gui协程_Python3进阶-协程
  5. JAVA开心超级签名系统源码+部署文档
  6. 增删改数据库表中的字段名
  7. pytorch的索引与切片
  8. Atitit 索引法 html文件转txt纯文本索引 适用于 evernote索引,导入imap邮箱,方便检索 /sumdoclist/src/aPkg/html2txtIndexFile.jav
  9. linux删除用户名命令,linux删除用户命令
  10. PPT_设计师的十大秘诀
  11. AD PCBlayout 总结
  12. BizTalk Server : 提高 BizTalk 编程能力的 8 点技巧和窍门
  13. 小米4android8.0root,小米8青春版获取root权限的教程
  14. EXCEL POI单元格下拉的两种实现方式
  15. VB 从零开始编外挂
  16. mysql on.000002_mysql | 同乐学堂
  17. SQL中UNPIVOT是什么
  18. uni-app开发小程序并运行起来(使用ColorUI)
  19. 分布式:Docker
  20. 冯诺依曼原理奠定了至今仍然在使用的计算机,计算机二级MS-Office真题「选择题」...

热门文章

  1. Photoshop赶上“元宇宙”快车,宝藏SaaS软件大盘点
  2. 基于PP-YOLOE的雾天行人车辆目标检测
  3. 树莓派linux定时任务,树莓派如何定时关机或定时执行任务
  4. win11 移动热点无法打开,求指点啊
  5. 高中社会实践活动 计算机,【计算机】社会实践 | 回访母校,感恩母校
  6. Python-序列化和反序列化
  7. 树莓派 2 和 3 上的 Swift 3.0
  8. 木框木字的制作——盗墓笔记
  9. 杰克逊追悼会众星致辞献唱 数十亿歌迷痛别
  10. 0x000007FEFD72A06D 处(位于 Opencv_Test.exe 中)引发的异常: Microsoft C++ 异常: cv::Exception