Sqlalchemy - for循环中查询和查询之间的区别

时间:2009-07-03 08:34:17

标签: sqlalchemy

我想问一下

之间的区别
for row in session.Query(Model1):
    pass

for row in session.Query(Model1).all():
    pass

是第一个以某种方式用单个查询轰炸你的数据库的迭代器,后者“急切地”将整个事件作为一个列表查询(比如range(x)vs xrange(x))?

2 个答案:

答案 0 :(得分:87)

不,数据库流量没有差异。不同之处在于for row in session.Query(Model1) ORM在每一行即将提供给你的时候都会工作,而for row in session.Query(Model1).all()在开始向你提供ORM之前对所有行都有效。< / p>

请注意q.all()只是list(q)的糖,即将生成器产生的所有内容收集到列表中。以下是Query类中source code的{{3}}(在链接源中查找def all):

def all(self):
    """Return the results represented by this ``Query`` as a list.

    This results in an execution of the underlying query.

    """
    return list(self)

...其中self,查询对象,是一个可迭代的,即具有__iter__方法。

从逻辑上讲,这两种方式在数据库流量方面完全相同;最终都调用query.__iter__()来获取行迭代器,并next()通过它。

实际的不同之处在于前者可以在数据到达后立即开始为您提供行,将数据库结果集“流式传输”给您,减少内存使用和延迟。我无法确定所有当前的引擎实现都是这样做的(我希望他们这样做!)。在任何情况下,后一版本都没有充分的理由阻止了这种效率。

答案 1 :(得分:3)

实际上,接受的响应不是真的(或者至少不再是真的),特别是以下陈述是错误的:

  

(1)的区别仅在于session.Query(Model1).all()中的行。Query(Model1)在将要提供给您的行上进行ORM工作,而session.Query(Model1).all( )在开始提供给您之前,ORM是否在所有行上都起作用。

无论选择使用2个选项中的哪一个,SQLAlchemy始终将ORM映射所有行。可以在these lines中的源代码中看到; loading.instances方法确实将返回一个生成器,但是返回一个已经映射的ORM实例;您可以在实际的generator looping code中确认这一点:

for row in rows: #  ``rows`` here are already ORM mapped rows
    yield row

因此,在生成器的第一次运行完成并产生一个实例时,所有实例都已进行ORM映射。 (以下示例(1))

  

(2)实际的区别是,前者可以在数据到达后立即开始为您提供行,从而将DB结果集“流式传输”给您,从而减少了内存使用和延迟。

如上所述,这也是错误的,因为all data retrieved from the DB将在完成任何屈服之前进行处理/映射。

这些评论也具有误导性。具体来说:

  

数据库命中可能是相同的,但是请记住,所有操作都会将整个结果集加载到内存中。这可能是千兆字节的数据

无论是否使用.all(),都会加载整个结果集。在this line中清晰可见。这也非常容易测试/验证。

我从陈述的2个选项中看到的唯一区别是,使用.all会在生成器首先被迭代/耗尽时在结果中循环两次(如果要处理所有实例/行)。通过调用list(self)将其转换为列表。

由于SQLAlchemy代码不容易理解,因此我写了一个简短的代码段来举例说明所有这一切:

class Query:
    def all(self):
        return list(self)

    def instances(self, rows_to_fetch=5):
        """ORM instance generator"""
        mapped_rows = []
        for i in range(rows_to_fetch):
            # ORM mapping work here as in lines 81-88 from loading.instances
            mapped_rows.append(i)
        print("orm work finished for all rows")
        for row in mapped_rows:  # same as ``yield from mapped_rows``
            print("yield row")
            yield row

    def __iter__(self):
        return self.instances()


query = Query()
print("(1) Generator scenario:")
print("First item of generator: ", next(iter(query)))

print("\n(2) List scenario:")
print("First item of list: ", query.all()[0])

"""
RESULTS:
--------
(1) Generator scenario:
orm work finished for all rows
yield row
First item of generator:  0

(2) List scenario:
orm work finished for all rows
yield row
yield row
yield row
yield row
yield row
First item of list:  0
"""

为了在处理大型结果集时拥有更好的控制,例如,必须使用类似yield_per的方法,但这仍然不是一个一例的场景,而是分批执行ORM映射。实例。

值得一提的是{em {em} ,并且很容易被忽略(因此写了这个答案),指向迈克尔·拜耳的only comment。 / p>

相关问题