2012-03-14 94 views
2

我正在優化我們的(第一個)Django項目中的緩慢頁面加載。整個項目不會測試狀態管理,所以有些協議會有計劃執行的情況。目前的代碼是:Django:我如何避免不必要的SQL語句?

protocols = Protocol.active.filter(team=team, release=release) 
cases = Case.active.filter(protocol__in=protocols) 
caseCount = cases.count() 
plannedExecs = Planned_Exec.active.filter(case__in=cases, team=team, release=release) 

# Start aggregating test suite information 
# pgi Model 
testSuite['pgi_model'] = [] 
for pgi in PLM.objects.filter(release=release).values('pgi_model').distinct(): 
    plmForPgi = PLM.objects.filter(pgi_model=pgi['pgi_model']) 
    peresults = plannedExecs.filter(plm__in=plmForPgi).count() 
    if peresults > 0: 
     try: 
      testSuite['pgi_model'].append((pgi['pgi_model'], "", "", peresults, int(peresults/float(testlistCount)*100))) 
     except ZeroDivisionError: 
      testSuite['pgi_model'].append((pgi['pgi_model'], "", "", peresults, 0)) 

# Browser 
testSuite['browser'] = [] 
for browser in BROWSER_OPTIONS: 
    peresults = plannedExecs.filter(browser=browser[0]).count() 
    try: 
     testSuite['browser'].append((browser[1], "", "", peresults, int(peresults/float(testlistCount)*100))) 
    except ZeroDivisionError: 
     testSuite['browser'].append((browser[1], "", "", peresults, 0)) 

# ... more different categories are aggregated below, then the report is generated... 

該代碼製作了大量的SQL語句。 PLM.objects.filter(release=release).values('pgi_model').distinct()返回一個包含50個字符串的列表,並且這兩個過濾器操作都爲每個字符串執行一條SQL語句,這意味着100個SQL語句僅用於此循環。 (另外,它似乎應該使用values_listflat=True。)

由於我想獲取有關相關案件和計劃執行信息,我想我只需要檢索這兩個表,然後對此進行一些分析。使用filter和count()似乎是當時顯而易見的解決方案,但我想知道如果使用.values()構建相關案例和計劃執行信息的代碼,然後再分析它,那麼是不是更好?以避免不必要的SQL語句。任何有用的建議?謝謝!

編輯:在試圖分析這個以瞭解時間在哪裏,我使用Django Debug工具欄。它解釋說,有超過200個查詢,並且每個查詢都運行得非常快,所以總的來說它們佔用的時間很少。但是,SQL的執行速度是否相對較快,但是ORM的構建會增加,因爲它發生了200次以上?我重構了一個需要花費3分鐘才能加載的上一頁,並使用values()而不是ORM,從而將頁面加載到2.7秒和5條SQL語句。

+1

你或許應該配置文件,看是否放緩是由於SQL,或到Python代碼。 – Oliver 2012-03-14 15:25:17

+0

剛剛編輯我的答案 - 我使用Django Debug工具欄進行分析,這意味着SQL調用都發生得相對較快,但很難看出時間在哪裏。您使用哪些工具進行分析? – Nathan 2012-03-14 15:40:24

+0

您使用的數據庫是? – 2012-03-14 17:30:13

回答

1

創建一個查詢集不會觸及數據庫;只能從中訪問結果。因此,僅僅創建查詢集不是你的問題。

請注意,將查詢集傳遞給另一個查詢集不會創建兩個查詢。因此,構建命令不會減少數據庫命中的數量。

如果您可以構建字典,那麼您可能會設法創建比您更簡單的查詢,這會加快實際的查詢執行速度。然而,這是一個單獨的問題。

+0

權限 - 訪問結果是命中數據庫的結果。在這個實例中使用Django的ORM訪問結果的自然方式似乎是構建超過200個不同的查詢集。除了在ORM之外構建字典之外,是否有辦法避免這種情況? – Nathan 2012-03-14 15:51:58

+0

@Nathan我重複一遍:構建查詢集不會觸及數據庫。您必須訪問查詢集的內容。請注意,將查詢集傳遞給另一個查詢集不會創建兩個查詢。因此,構建命令不會減少數據庫命中的數量。 – Marcin 2012-03-14 16:02:11

+1

我構建了200個不同的查詢集並從每個查詢集訪問數據。我瀏覽了代碼並觀看了SQL日誌,所以我知道你的意思。減少SQL語句數量的一種方法是通過使用'values()'而不是過濾來同時獲取數據庫的所有相關行,然後在Python中使用對象。 – Nathan 2012-03-14 16:08:42

1

這讓我想起反向外鍵查找的情況。我們應該能夠通過在版本中獲取與PLM關聯的所有pgi_models來減少循環的頂部。我假設你有一個PGI模型,PLM模型有一個名爲pgi_model的外鍵字段。如果是這種情況,您可以通過以下方式在PLM版本中找到PGI。你仍然有一個循環,但循環迭代應減少,理論上:

pgis = PGI.objects.filter(plm__in=PLM.objects.filter(release=release)) 
for pgi in pgis: 
    peresults = plannedExecs.filter(plm=pgi.plm).count() 
    if peresults > 0: 
     try: 
      testSuite['pgi_model'].append((pgi['pgi_model'], "", "", peresults, int(peresults/float(testlistCount)*100))) 
     except ZeroDivisionError: 
      testSuite['pgi_model'].append((pgi['pgi_model'], "", "", peresults, 0))