Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 在Django中,检查空查询集最有效的方法是什么?_Sql_Django_Performance_Django Queryset - Fatal编程技术网

Sql 在Django中,检查空查询集最有效的方法是什么?

Sql 在Django中,检查空查询集最有效的方法是什么?,sql,django,performance,django-queryset,Sql,Django,Performance,Django Queryset,我听到了使用以下内容的建议: if qs.exists(): ... if qs.count(): ... try: qs[0] except IndexError: ... 从下面的注释中复制:“我正在寻找一个类似于”的语句,在MySQL和PostgreSQL count()中,短查询速度更快,exists()中,长查询速度更快,当您可能需要第一个元素并且希望检查它是否存在时,请使用QuerySet[0]。但是,当count()更快时,它只会稍微快一点,因

我听到了使用以下内容的建议:

if qs.exists():
    ...

if qs.count():
    ...

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

从下面的注释中复制:“我正在寻找一个类似于”的语句,在MySQL和PostgreSQL count()中,短查询速度更快,exists()中,长查询速度更快,当您可能需要第一个元素并且希望检查它是否存在时,请使用QuerySet[0]。但是,当count()更快时,它只会稍微快一点,因此建议在两者之间进行选择时始终使用exists()。

我认为第一种方法是最有效的方法(您可以很容易地用第二种方法实现它,因此它们可能几乎相同).最后一个需要从数据库中实际获取整个对象,因此几乎可以肯定这是最昂贵的


但是,像所有这些问题一样,了解特定数据库、模式和数据集的唯一方法是自己测试它。

这取决于使用上下文

根据:

使用QuerySet.count() …如果您只需要计数,而不是执行len(queryset)

使用QuerySet.exists() …如果您只想确定是否至少存在一个结果,而不是查询集

但是:

不要过度使用count()和exists() 如果您需要来自QuerySet的其他数据,只需对其进行评估

因此,如果您只想检查一个空的QuerySet,我认为
QuerySet.exists()
是最推荐的方法。另一方面,如果您想稍后使用结果,最好对其进行评估

我还认为您的第三个选项是最昂贵的,因为您需要检索所有记录以检查是否存在任何记录。

exists()通常比count()快,但并不总是如此(请参见下面的测试)。count()可用于检查记录的存在性和长度

如果您确实需要对象,请仅使用
qs[0]
。如果您只是测试对象是否存在,则速度会明显减慢

在Amazon SimpleDB上,400000行:

  • 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/通行证
  • qs.exists()
    :1.21 usec/pass
  • qs.count()
    :1.16 usec/pass
  • qs[0]
    :1.27 usec/pass
我对每个过程使用随机查询,以降低db级缓存的风险。测试代码:

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)
query.exists()
是最有效的方法

尤其是在postgres上,
count()
可能非常昂贵,有时比普通的select查询更昂贵

exists()
运行一个查询,没有选择、字段选择或排序,只获取一条记录。这比使用表联接和排序计算整个查询要快得多

qs[0]
仍将包括与选择相关的字段选择和排序;因此成本会更高

Django源代码如下(Django/db/models/sql/query.py RawQuery.has_results):

前几天我遇到的另一个问题是在if语句中调用QuerySet。它执行并返回整个查询

如果变量查询集可能是
None
(未设置函数参数),则使用:

if query_set is None:
    # 
不是:

这是一个不错的起点,但该方法存在一些缺陷,即:

  • 随机IP地址可能最终匹配0或很少的结果
  • 异常会扭曲结果,因此我们应该避免处理异常
  • 因此,我没有过滤可能匹配的内容,而是决定排除肯定不匹配的内容,希望仍然避免使用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]
    也很快,但它需要异常处理。

    我也遇到了这个问题。是的
    exists()
    在大多数情况下速度更快,但这在很大程度上取决于您尝试执行的查询集的类型。例如,对于以下简单查询:
    my\u objects=MyObject.objets.all()
    您会使用
    my\u objects.exists()
    。但是如果您要执行以下查询:
    MyObject.objects.filter(some\u attr='anywhere')。exclude(something='what')。distinct('key')。value()
    可能需要测试哪一个更适合(
    exists()
    count()
    len(我的对象)
    )。请记住,DB引擎将执行查询,要获得良好的性能结果,这在很大程度上取决于数据结构和查询的形成方式。您可以做的一件事是,审核查询并根据DB引擎对其进行测试,然后将结果与django进行比较。您会惊讶于django有时有多么幼稚,请尝试
    QueryCountMiddleware
    查看执行的所有查询,您将看到我所说的内容。

    我非常确定最后一个选项只获得一条记录,而不是所有记录。此外,从文档中还不清楚存在的速度是否快于计数。如果是,我想知道速度增量,以及它是否变化很大y基于查询集的长度等条件。你是对的-最后一个选项将被转换为类似于
    从bar LIMIT 1选择foo
    。这里我们要说的是-所有这些方法都是ORM API调用并转换为SQL查询。这就是为什么文档不能说
    存在
    count
    。或者它的速度快了多少倍。这取决于这些数据库的实现、配置和相关数据集。我认为clauless是正确的-您需要自己测试它才能获得
    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