[英]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):
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 可能是None
( None
設置函數的參數),則使用:
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/passqs.exists()
: 144.46 使用 c/passqs.count()
144.33 usec/passqs[0]
: 324.98 使用 c/pass在 MySQL 上,57 行:
qs
: 1.07 usec/passqs.exists()
: 1.21 usec/passqs.count()
: 1.16 usec/passqs[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 的解決方案是一個不錯的起點,但該方法存在一些缺陷,即:
因此,我沒有過濾可能匹配的內容,而是決定排除肯定不匹配的內容,希望仍然避免使用 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.