我听说过使用以下内容的建议:
if qs.exists():
...
if qs.count():
...
try:
qs[0]
except IndexError:
...
从下面的评论复制:“我正在寻找一个声明,例如”在MySQL中,PostgreSQL count()对于短查询更快,exists()对于长查询更快,并且在可能的情况下使用QuerySet [0]你将需要第一个元素,并且你想检查它是否存在。但是,当count()更快时,它只会稍微快一些,因此建议在两者之间选择时始终使用exists()。“
答案 0 :(得分:15)
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
(函数的未设置参数),那么使用:
if query_set is None:
#
不是:
if query_set:
# you just hit the database
答案 1 :(得分:14)
看起来像 qs.count()和qs.exists()实际上是等效的。因此我没有发现使用exists()而不是count()的原因。后者并不慢,它可以用来检查存在和长度。存在()和count()可能会在MySQL中对同一个查询进行求值。
如果您确实需要该对象,请仅使用qs[0]
。如果你只是测试存在,它会慢得多。
在Amazon SimpleDB上,400,000行:
qs
:325.00 usec / pass qs.exists()
:144.46 usec / pass qs.count()
144.33 usec / pass qs[0]
:324.98 usec / 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)
答案 2 :(得分:7)
这取决于使用环境。
使用QuerySet.count()
...如果你只想要计数,而不是做len(queryset)。
使用QuerySet.exists()
...如果您只想知道是否存在至少一个结果,而不是查询。
可是:
不要过度使用count()和exists()
如果您需要QuerySet中的其他数据,只需对其进行评估。
所以,我认为如果你只想检查一个空的QuerySet,QuerySet.exists()
是最推荐的方法。另一方面,如果您想稍后使用结果,最好对其进行评估。
我还认为您的第三个选项是最昂贵的,因为您需要检索所有记录以检查是否存在。
答案 3 :(得分:4)
@Sam Odio's solution是一个不错的起点,但方法中存在一些缺陷,即:
因此,我没有过滤掉可能匹配的东西,而是决定排除一些绝对不匹配的东西,希望仍能避免数据库缓存,同时也要确保相同的行数。
我只使用数据集:
对本地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]
也很快,但需要处理异常。
答案 4 :(得分:1)
我认为第一种方法是最有效的方法(你可以根据第二种方法轻松实现它,所以它们可能几乎相同)。最后一个实际上需要从数据库中获取一个完整的对象,所以它几乎肯定是最昂贵的。
但是,就像所有这些问题一样,了解特定数据库,架构和数据集的唯一方法就是自己测试它。
答案 5 :(得分:0)
我也遇到了麻烦。是的,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)
)。请记住,DB引擎是执行查询的引擎,要获得良好的性能结果,它很大程度上取决于数据结构和查询的形成方式。您可以做的一件事是,审核查询并针对数据库引擎自己对它们进行测试,然后比较您的结果,有时django的天真性会让您感到惊讶,请尝试QueryCountMiddleware
查看所有执行的查询,然后您会明白我在说什么。