簡體   English   中英

在 Django 中,檢查空查詢集的最有效方法是什么?

[英]In Django, what is the most efficient way to check for an empty query set?

我聽說過使用以下內容的建議:

if qs.exists():
    ...

if qs.count():
    ...

try:
    qs[0]
except IndexError:
    ...

從下面的評論中復制:“我正在尋找這樣的語句”在 MySQL 和 PostgreSQL 中,count() 對於短查詢更快,exists() 對於長查詢更快,並且在您可能需要時使用 QuerySet[0]將需要第一個元素並且您想檢查它是否存在。 但是,當 count() 更快時,它只會稍微快一點,因此建議在兩者之間進行選擇時始終使用exists()。”

query.exists()是最有效的方式。

特別是在 postgres count()上可能非常昂貴,有時比普通的選擇查詢更昂貴。

exists()運行一個沒有 select_related、字段選擇或排序的查詢,並且只獲取一條記錄。 這比使用表連接和排序計算整個查詢要快得多。

qs[0]仍將包括 select_related、字段選擇和排序; 所以會更貴。

Django 源代碼在這里(django/db/models/sql/query.py RawQuery.has_results):

https://github.com/django/django/blob/60e52a047e55bc4cd5a93a8bd4d07baed27e9a22/django/db/models/sql/query.py#L499

def has_results(self, using):
    q = self.clone()
    if not q.distinct:
        q.clear_select_clause()
    q.clear_ordering(True)
    q.set_limits(high=1)
    compiler = q.get_compiler(using=using)
    return compiler.has_results()

前幾天讓我遇到的另一個問題是在 if 語句中調用 QuerySet。 執行並返回整個查詢!

如果變量 query_set 可能是NoneNone設置函數的參數),則使用:

if query_set is None:
    # 

不是:

if query_set:
   # you just hit the database

exists() 通常比 count() 快,但並非總是如此(請參閱下面的測試)。 count() 可用於檢查是否存在和長度。

僅當您確實需要該對象時才使用qs[0] 如果您只是在測試是否存在,它會明顯變慢。

在 Amazon SimpleDB 上,400,000 行:

  • qs : 325.00 usec/pass
  • qs.exists() : 144.46 使用 c/pass
  • qs.count() 144.33 usec/pass
  • qs[0] : 324.98 使用 c/pass

在 MySQL 上,57 行:

  • qs : 1.07 usec/pass
  • qs.exists() : 1.21 usec/pass
  • qs.count() : 1.16 usec/pass
  • qs[0] : 1.27 usec/pass

我對每次傳遞使用隨機查詢來降低數據庫級緩存的風險。 測試代碼:

import timeit

base = """
import random
from plum.bacon.models import Session
ip_addr = str(random.randint(0,256))+'.'+str(random.randint(0,256))+'.'+str(random.randint(0,256))+'.'+str(random.randint(0,256))
try:
    session = Session.objects.filter(ip=ip_addr)%s
    if session:
        pass
except:
    pass
"""

query_variatons = [
    base % "",
    base  % ".exists()",
    base  % ".count()",
    base  % "[0]"
    ]

for s in query_variatons:
    t = timeit.Timer(stmt=s)
    print "%.2f usec/pass" % (1000000 * t.timeit(number=100)/100000)

這取決於使用上下文。

根據文檔

使用 QuerySet.count()

...如果你只想要計數,而不是做 len(queryset)。

使用 QuerySet.exists()

...如果您只想找出是否存在至少一個結果,而不是查詢集是否存在。

但:

不要過度使用 count() 和 exists()

如果您需要來自 QuerySet 的其他數據,只需評估它。

因此,如果您只想檢查空的 QuerySet,我認為QuerySet.exists()是最推薦的方法。 另一方面,如果您想稍后使用結果,最好對其進行評估。

我還認為您的第三個選項是最昂貴的,因為您需要檢索所有記錄以檢查是否存在任何記錄。

@Sam Odio 的解決方案是一個不錯的起點,但該方法存在一些缺陷,即:

  1. 隨機 IP 地址可能最終匹配 0 個或很少的結果
  2. 異常會扭曲結果,所以我們應該盡量避免處理異常

因此,我沒有過濾可能匹配的內容,而是決定排除肯定不匹配的內容,希望仍然避免使用 DB 緩存,但也確保相同的行數。

我只針對本地 MySQL 數據庫進行了測試,數據集為:

>>> Session.objects.all().count()
40219

計時碼:

import timeit
base = """
import random
import string
from django.contrib.sessions.models import Session
never_match = ''.join(random.choice(string.ascii_uppercase) for _ in range(10))
sessions = Session.objects.exclude(session_key=never_match){}
if sessions:
    pass
"""
s = base.format('count')

query_variations = [
    "",
    ".exists()",
    ".count()",
    "[0]",
]

for variation in query_variations:
    t = timeit.Timer(stmt=base.format(variation))
    print "{} => {:02f} usec/pass".format(variation.ljust(10), 1000000 * t.timeit(number=100)/100000)

輸出:

           => 1390.177710 usec/pass
.exists()  => 2.479579 usec/pass
.count()   => 22.426991 usec/pass
[0]        => 2.437079 usec/pass

所以你可以看到,對於這個數據集, count()大約比exists()慢9 倍。

[0]也很快,但需要異常處理。

我想第一種方法是最有效的方法(你可以很容易地用第二種方法實現它,所以也許它們幾乎相同)。 最后一個實際上需要從數據庫中獲取整個對象,因此幾乎可以肯定它是最昂貴的。

但是,就像所有這些問題一樣,了解您的特定數據庫、模式和數據集的唯一方法是自己測試。

我也遇到了這個麻煩。 Yes exists()在大多數情況下更快,但它在很大程度上取決於您嘗試執行的查詢集的類型。 例如,對於像這樣的簡單查詢: my_objects = MyObject.objets.all()您將使用my_objects.exists() 但是,如果您要執行如下查詢: MyObject.objects.filter(some_attr='anything').exclude(something='what').distinct('key').values()您可能需要測試哪個適合更好( exists()count()len(my_objects) )。 請記住,數據庫引擎是執行查詢的引擎,要獲得良好的性能結果,很大程度上取決於數據結構和查詢的形成方式。 您可以做的一件事是,審核查詢並針對數據庫引擎自行測試它們並比較您的結果,您會驚訝於 django 有時是多么天真,嘗試使用QueryCountMiddleware查看執行的所有查詢,您將看到我所做的我在談論。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM