这是本系列的第二篇,内容是 prefetch_related() 函数的用途、实现途径、以及使用方法。
本系列的第一篇在这里
对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。或许你会说,没有一个叫OneToManyField的东西啊。实际上 ,ForeignKey就是一个多对一的字段,而被ForeignKey关联的字段就是一对多字段了。
prefetch_related()和select_related()的设计目的很相似,都是为了减少SQL查询的数量,但是实现的方式不一样。后者是通过JOIN语句,在SQL查询内解决问题。但是对于多对多关系,使用SQL语句解决就显得有些不太明智,因为JOIN得到的表将会很长,会导致SQL语句运行时间的增加和内存占用的增加。若有n个对象,每个对象的多对多字段对应Mi条,就会生成Σ(n)Mi 行的结果表。
prefetch_related()的解决方法是,分别查询每个表,然后用Python处理他们之间的关系。继续以上边的例子进行说明,如果我们要获得张三所有去过的城市,使用prefetch_related()应该是这么做:
1234 | >>> zhangs = Person.objects.prefetch_related(\’visitation\’).get(firstname=u\”张\”,lastname=u\”三\”)>>> for city in zhangs.visitation.all() :... print city... |
上述代码触发的SQL查询如下:
12345678910 | SELECT `QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`,`QSOptimize_person`.`lastname`, `QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id` FROM `QSOptimize_person` WHERE (`QSOptimize_person`.`lastname` = \’三\’ AND `QSOptimize_person`.`firstname` = \’张\’); SELECT (`QSOptimize_person_visitation`.`person_id`) AS `_prefetch_related_val`, `QSOptimize_city`.`id`, `QSOptimize_city`.`name`, `QSOptimize_city`.`province_id` FROM `QSOptimize_city` INNER JOIN `QSOptimize_person_visitation` ON (`QSOptimize_city`.`id` = `QSOptimize_person_visitation`.`city_id`)WHERE `QSOptimize_person_visitation`.`person_id` IN (1); |
第一条SQL查询仅仅是获取张三的Person对象,第二条比较关键,它选取关系表QSOptimize_person_visitation
中person_id
为张三的行,然后和city
表内联(INNER JOIN 也叫等值连接)得到结果表。
123456789101112131415 | +——+—————–+—————+——————–+—————–+| id | firstname | lastname | hometown_id | living_id |+——+—————–+—————+——————–+—————–+| 1 | 张 | 三 | 3 | 1 |+——+—————–+—————+——————–+—————–+1 row in set (0.00 sec) +———————————–+——+—————–+——————–+| _prefetch_related_val | id | name | province_id |+———————————–+——+—————–+——————–+| 1 | 1 | 武汉市 | 1 || 1 | 2 | 广州市 | 2 || 1 | 3 | 十堰市 | 1 |+———————————–+——+—————–+——————–+3 rows in set (0.00 sec) |
显然张三武汉、广州、十堰都去过。
又或者,我们要获得湖北的所有城市名,可以这样:
1234 | >>> hb = Province.objects.prefetch_related(\’city_set\’).get(name__iexact=u\”湖北省\”)>>> for city in hb.city_set.all():... =\”crayon-sy\”>.city_set.all():...
3. prefetch_related()对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。或许你会说,没有一个叫OneToManyField的东西啊。实际上 ,ForeignKey就是一个多对一的字段,而被ForeignKey关联的字段就是一对多字段了。
作用和方法prefetch_related()和select_related()的设计目的很相似,都是为了减少SQL查询的数量,但是实现的方式不一样。后者是通过JOIN语句,在SQL查询内解决问题。但是对于多对多关系,使用SQL语句解决就显得有些不太明智,因为JOIN得到的表将会很长,会导致SQL语句运行时间的增加和内存占用的增加。若有n个对象,每个对象的多对多字段对应Mi条,就会生成Σ(n)Mi 行的结果表。
prefetch_related()的解决方法是,分别查询每个表,然后用Python处理他们之间的关系。继续以上边的例子进行说明,如果我们要获得张三所有去过的城市,使用prefetch_related()应该是这么做:
上述代码触发的SQL查询如下:
第一条SQL查询仅仅是获取张三的Person对象,第二条比较关键,它选取关系表
显然张三武汉、广州、十堰都去过。 又或者,我们要获得湖北的所有城市名,可以这样:
|