欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

利用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())
...

顯然這不是一個明智的選擇,因為這樣做會導致1+(湖北省城市數(shù))次SQL查詢。反正是個反例,導致的查詢和獲得掉結(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,所以會導致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)文章

  • 梅爾頻率倒譜系數(shù)(mfcc)及Python實現(xiàn)

    梅爾頻率倒譜系數(shù)(mfcc)及Python實現(xiàn)

    這篇文章主要為大家詳細介紹了語音識別之梅爾頻率倒譜系數(shù)及Python實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • python自動循環(huán)定時開關(guān)機(非重啟)測試

    python自動循環(huán)定時開關(guān)機(非重啟)測試

    這篇文章主要為大家詳細介紹了python自動循環(huán)定時開關(guān)機(非重啟)測試,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Pycharm Terminal 與Project interpreter 安裝包不同步問題解決

    Pycharm Terminal 與Project interpreter 安裝

    本文主要介紹了Pycharm Terminal 與Project interpreter 安裝包不同步問題解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • Python實現(xiàn)中文文本關(guān)鍵詞抽取的三種方法

    Python實現(xiàn)中文文本關(guān)鍵詞抽取的三種方法

    文本關(guān)鍵詞抽取,是對文本信息進行高度凝練的一種有效手段,通過3-5個詞語準確概括文本的主題,幫助讀者快速理解文本信息,本文分別采用TF-IDF方法、TextRank方法和Word2Vec詞聚類方法,利用Python語言進行開發(fā),實現(xiàn)文本關(guān)鍵詞的抽取,需要的朋友可以參考下
    2024-01-01
  • python遞歸下載文件夾下所有文件

    python遞歸下載文件夾下所有文件

    這篇文章主要為大家詳細介紹了python遞歸下載文件夾下所有文件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • 編寫自定義的Django模板加載器的簡單示例

    編寫自定義的Django模板加載器的簡單示例

    這篇文章主要介紹了編寫自定義的Django模板加載器的簡單示例,Django是各色人氣Python框架中最為著名的一個,需要的朋友可以參考下
    2015-07-07
  • Python hashlib模塊用法實例分析

    Python hashlib模塊用法實例分析

    這篇文章主要介紹了Python hashlib模塊用法,結(jié)合實例形式分析了Python使用hash模塊進行md5、sha1、sha224、sha256、sha512等加密運算相關(guān)操作技巧與注意事項,需要的朋友可以參考下
    2018-06-06
  • Python數(shù)據(jù)庫格式化輸出文檔的思路與方法

    Python數(shù)據(jù)庫格式化輸出文檔的思路與方法

    這篇文章主要給大家介紹了關(guān)于Python數(shù)據(jù)庫格式化輸出文檔的思路與方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • Pyinstaller打包Scrapy項目的實現(xiàn)步驟

    Pyinstaller打包Scrapy項目的實現(xiàn)步驟

    這篇文章主要介紹了Pyinstaller打包Scrapy項目的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • 手寫一個python迭代器過程詳解

    手寫一個python迭代器過程詳解

    這篇文章主要介紹了手寫一個python迭代器過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-08-08

最新評論