最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

利用Django框架中select_related和prefetch_related函數(shù)對數(shù)據(jù)庫查詢優(yōu)化

 更新時間:2015年04月01日 15:38:49   作者:CuGBabyBeaR  
這篇文章主要介紹了利用Python的Django框架中select_related和prefetch_related函數(shù)對數(shù)據(jù)庫查詢的優(yōu)化的一個實踐例子,展示如何在實際中利用這兩個函數(shù)減少對數(shù)據(jù)庫的查詢次數(shù),需要的朋友可以參考下

實例的背景說明

假定一個個人信息系統(tǒng),需要記錄系統(tǒng)中各個人的故鄉(xiāng)、居住地、以及到過的城市。數(shù)據(jù)庫設(shè)計如下:

201541150650059.jpg (591×250)

Models.py 內(nèi)容如下:
 

from django.db import models
 
class Province(models.Model):
 name = models.CharField(max_length=10)
 def __unicode__(self):
  return self.name
 
class City(models.Model):
 name = models.CharField(max_length=5)
 province = models.ForeignKey(Province)
 def __unicode__(self):
  return self.name
 
class 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

注1:創(chuàng)建的app名為“QSOptimize”

注2:為了簡化起見,`qsoptimize_province` 表中只有2條數(shù)據(jù):湖北省和廣東省,`qsoptimize_city`表中只有三條數(shù)據(jù):武漢市、十堰市和廣州市

如果我們想要獲得所有家鄉(xiāng)是湖北的人,最無腦的做法是先獲得湖北省,再獲得湖北的所有城市,最后獲得故鄉(xiāng)是這個城市的人。就像這樣:
 

>>> hb = Province.objects.get(name__iexact=u"湖北省")
>>> people = []
>>> for city in hb.city_set.all():
... people.extend(city.birth.all())
...

顯然這不是一個明智的選擇,因為這樣做會導(dǎo)致1+(湖北省城市數(shù))次SQL查詢。反正是個反例,導(dǎo)致的查詢和獲得掉結(jié)果就不列出來了。
prefetch_related() 或許是一個好的解決方法,讓我們來看看。
 

>>> hb = Province.objects.prefetch_related("city_set__birth").objects.get(name__iexact=u"湖北省")
>>> people = []
>>> for city in hb.city_set.all():
... people.extend(city.birth.all())
...

因為是一個深度為2的prefetch,所以會導(dǎo)致3次SQL查詢:
 

SELECT `QSOptimize_province`.`id`, `QSOptimize_province`.`name`
FROM `QSOptimize_province`
WHERE `QSOptimize_province`.`name` LIKE '湖北省' ;
 
SELECT `QSOptimize_city`.`id`, `QSOptimize_city`.`name`, `QSOptimize_city`.`province_id`
FROM `QSOptimize_city`
WHERE `QSOptimize_city`.`province_id` IN (1);
 
SELECT `QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`, `QSOptimize_person`.`lastname`,
`QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id`
FROM `QSOptimize_person`
WHERE `QSOptimize_person`.`hometown_id` IN (1, 3);

嗯…看上去不錯,但是3次查詢么?倒過來查詢可能會更簡單?
 

>>> people = list(Person.objects.select_related("hometown__province").filter(hometown__province__name__iexact=u"湖北省"))
 
SELECT `QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`, `QSOptimize_person`.`lastname`,
`QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id`, `QSOptimize_city`.`id`,
`QSOptimize_city`.`name`, `QSOptimize_city`.`province_id`, `QSOptimize_province`.`id`, `QSOptimize_province`.`name`
FROM `QSOptimize_person`
INNER JOIN `QSOptimize_city` ON (`QSOptimize_person`.`hometown_id` = `QSOptimize_city`.`id`)
INNER JOIN `QSOptimize_province` ON (`QSOptimize_city`.`province_id` = `QSOptimize_province`.`id`)
WHERE `QSOptimize_province`.`name` LIKE '湖北省';
 
+----+-----------+----------+-------------+-----------+----+--------+-------------+----+--------+
| id | firstname | lastname | hometown_id | living_id | id | name | province_id | id | name |
+----+-----------+----------+-------------+-----------+----+--------+-------------+----+--------+
| 1 | 張  | 三  |   3 |   1 | 3 | 十堰市 |   1 | 1 | 湖北省 |
| 2 | 李  | 四  |   1 |   3 | 1 | 武漢市 |   1 | 1 | 湖北省 |
| 3 | 王  | 麻子  |   3 |   2 | 3 | 十堰市 |   1 | 1 | 湖北省 |
+----+-----------+----------+-------------+-----------+----+--------+-------------+----+--------+
3 rows in set (0.00 sec)

完全沒問題。不僅SQL查詢的數(shù)量減少了,python程序上也精簡了。
select_related()的效率要高于prefetch_related()。因此,最好在能用select_related()的地方盡量使用它,也就是說,對于ForeignKey字段,避免使用prefetch_related()。
聯(lián)用
對于同一個QuerySet,你可以同時使用這兩個函數(shù)。
在我們一直使用的例子上加一個model:Order (訂單)
 

class Order(models.Model):
 customer = models.ForeignKey(Person)
 orderinfo = models.CharField(max_length=50)
 time  = models.DateTimeField(auto_now_add = True)
 def __unicode__(self):
  return self.orderinfo

如果我們拿到了一個訂單的id 我們要知道這個訂單的客戶去過的省份。因為有ManyToManyField顯然必須要用prefetch_related()。如果只用prefetch_related()會怎樣呢?
 

>>> plist = Order.objects.prefetch_related('customer__visitation__province').get(id=1)
>>> for city in plist.customer.visitation.all():
... print city.province.name
...

顯然,關(guān)系到了4個表:Order、Person、City、Province,根據(jù)prefetch_related()的特性就得有4次SQL查詢
 

SELECT `QSOptimize_order`.`id`, `QSOptimize_order`.`customer_id`, `QSOptimize_order`.`orderinfo`, `QSOptimize_order`.`time`
FROM `QSOptimize_order`
WHERE `QSOptimize_order`.`id` = 1 ;
 
SELECT `QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`, `QSOptimize_person`.`lastname`, `QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id`
FROM `QSOptimize_person`
WHERE `QSOptimize_person`.`id` IN (1);
 
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);
 
SELECT `QSOptimize_province`.`id`, `QSOptimize_province`.`name`
FROM `QSOptimize_province`
WHERE `QSOptimize_province`.`id` IN (1, 2);
+----+-------------+---------------+---------------------+
| id | customer_id | orderinfo  | time    |
+----+-------------+---------------+---------------------+
| 1 |   1 | Info of Order | 2014-08-10 17:05:48 |
+----+-------------+---------------+---------------------+
1 row in set (0.00 sec)
 
+----+-----------+----------+-------------+-----------+
| 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)
 
+----+--------+
| id | name |
+----+--------+
| 1 | 湖北省 |
| 2 | 廣東省 |
+----+--------+
2 rows in set (0.00 sec)

更好的辦法是先調(diào)用一次select_related()再調(diào)用prefetch_related(),最后再select_related()后面的表
 

>>> plist = Order.objects.select_related('customer').prefetch_related('customer__visitation__province').get(id=1)
>>> for city in plist.customer.visitation.all():
... print city.province.name
...

這樣只會有3次SQL查詢,Django會先做select_related,之后prefetch_related的時候會利用之前緩存的數(shù)據(jù),從而避免了1次額外的SQL查詢:

SELECT `QSOptimize_order`.`id`, `QSOptimize_order`.`customer_id`, `QSOptimize_order`.`orderinfo`, 
`QSOptimize_order`.`time`, `QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`, 
`QSOptimize_person`.`lastname`, `QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id` 
FROM `QSOptimize_order` 
INNER JOIN `QSOptimize_person` ON (`QSOptimize_order`.`customer_id` = `QSOptimize_person`.`id`) 
WHERE `QSOptimize_order`.`id` = 1 ;
 
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);
 
SELECT `QSOptimize_province`.`id`, `QSOptimize_province`.`name` 
FROM `QSOptimize_province` 
WHERE `QSOptimize_province`.`id` IN (1, 2);
 
+----+-------------+---------------+---------------------+----+-----------+----------+-------------+-----------+
| id | customer_id | orderinfo  | time    | id | firstname | lastname | hometown_id | living_id |
+----+-------------+---------------+---------------------+----+-----------+----------+-------------+-----------+
| 1 |   1 | Info of Order | 2014-08-10 17:05:48 | 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)
 
+----+--------+
| id | name |
+----+--------+
| 1 | 湖北省 |
| 2 | 廣東省 |
+----+--------+
2 rows in set (0.00 sec)

值得注意的是,可以在調(diào)用prefetch_related之前調(diào)用select_related,并且Django會按照你想的去做:先select_related,然后利用緩存到的數(shù)據(jù)prefetch_related。然而一旦prefetch_related已經(jīng)調(diào)用,select_related將不起作用。

 小結(jié)

  1.     因為select_related()總是在單次SQL查詢中解決問題,而prefetch_related()會對每個相關(guān)表進行SQL查詢,因此select_related()的效率通常比后者高。
  2.     鑒于第一條,盡可能的用select_related()解決問題。只有在select_related()不能解決問題的時候再去想prefetch_related()。
  3.     你可以在一個QuerySet中同時使用select_related()和prefetch_related(),從而減少SQL查詢的次數(shù)。
  4.     只有prefetch_related()之前的select_related()是有效的,之后的將會被無視掉。

相關(guān)文章

  • Python字符串對象實現(xiàn)原理詳解

    Python字符串對象實現(xiàn)原理詳解

    這篇文章主要介紹了Python字符串對象實現(xiàn)原理詳解,在Python世界中將對象分為兩種:一種是定長對象,比如整數(shù),整數(shù)對象定義的時候就能確定它所占用的內(nèi)存空間大小,另一種是變長對象,在對象定義時并不知道是多少,需要的朋友可以參考下
    2019-07-07
  • python運行cmd命令行的3種方法總結(jié)

    python運行cmd命令行的3種方法總結(jié)

    雖然python在調(diào)用cmd命令方面使用的比較少,不過還是要用的,下面這篇文章主要給大家介紹了關(guān)于python運行cmd命令行的3種方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-09-09
  • python如何使用正則表達式的前向、后向搜索及前向搜索否定模式詳解

    python如何使用正則表達式的前向、后向搜索及前向搜索否定模式詳解

    這篇文章主要給大家介紹了關(guān)于python如何使用正則表達式的前向、后向搜索及前向搜索否定模式的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-11-11
  • Python 判斷是否為質(zhì)數(shù)或素數(shù)的實例

    Python 判斷是否為質(zhì)數(shù)或素數(shù)的實例

    下面小編就為大家?guī)硪黄狿ython 判斷是否為質(zhì)數(shù)或素數(shù)的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • python如何使用python-docx調(diào)整Word文檔樣式

    python如何使用python-docx調(diào)整Word文檔樣式

    本文介紹了如何修改文字字體樣式、對齊樣式、行間距以及段前段后間距,并通過效果展示給出參考,適用于需要進行文檔編輯的讀者
    2024-09-09
  • python實現(xiàn)的陽歷轉(zhuǎn)陰歷(農(nóng)歷)算法

    python實現(xiàn)的陽歷轉(zhuǎn)陰歷(農(nóng)歷)算法

    這篇文章主要介紹了python實現(xiàn)的陽歷轉(zhuǎn)陰歷(農(nóng)歷)算法,需要的朋友可以參考下
    2014-04-04
  • 詳解Python函數(shù)中的幾種參數(shù)

    詳解Python函數(shù)中的幾種參數(shù)

    這篇文章主要為大家介紹了Python參數(shù)的使用,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-12-12
  • Python 內(nèi)置方法和屬性詳解

    Python 內(nèi)置方法和屬性詳解

    這篇文章主要為大家介紹了Python 內(nèi)置方法和屬性,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-12-12
  • Python數(shù)據(jù)類型中的元組Tuple

    Python數(shù)據(jù)類型中的元組Tuple

    這篇文章主要介紹了Python數(shù)據(jù)類型中的元組Tuple,元組可以理解為一個只讀列表,用()來標(biāo)識,下文圍繞元組展開詳細資料,需要的小伙伴可以參考一下
    2022-02-02
  • Python編譯成.so文件進行加密后調(diào)用的實現(xiàn)

    Python編譯成.so文件進行加密后調(diào)用的實現(xiàn)

    今天小編就為大家分享一篇Python編譯成.so文件進行加密后調(diào)用的實現(xiàn),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12

最新評論

太和县| 修武县| 灌云县| 德昌县| 兴文县| 特克斯县| 黄梅县| 宁乡县| 滕州市| 淅川县| 林口县| 商洛市| 白河县| 嘉鱼县| 固原市| 夏津县| 凉山| 韶山市| 辽宁省| 梅州市| 松阳县| 正镶白旗| 龙泉市| 塘沽区| 商水县| 广河县| 普兰店市| 改则县| 芦溪县| 三穗县| 英山县| 东兴市| 清镇市| 长沙市| 信丰县| 湖州市| 雷州市| 沂南县| 密山市| 克拉玛依市| 前郭尔|