84

我想问一下有什么区别

for row in session.Query(Model1):
    pass

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

是第一个以某种方式用单个查询轰炸您的数据库的迭代器,而后者“渴望”将整个事物作为列表查询(如 range(x) 与 xrange(x))?

4

2 回答 2

104

不,数据库流量没有区别。不同之处在于,for row in session.Query(Model1)当 ORM 将要提供给您时,它是否在每一行上工作,而for row in session.Query(Model1).all()ORM 在开始提供给您之前是否在所有行上工作。

请注意,这q.all()只是糖list(q),即将生成器产生的所有内容收集到一个列表中。这是它的源代码,在Query类中(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()通过它。

实际的区别是前者可以在数据到达后立即开始为您提供行,将数据库结果集“流式传输”给您,减少内存使用和延迟。我不能确定所有当前的引擎实现都这样做(我希望他们这样做!)。无论如何,后一个版本无缘无故地阻止了这种效率。

于 2009-07-03T17:01:02.060 回答
19

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

(1) 不同之处在于,对于 session.Query(Model1) 中的行,当 ORM 将要提供给您时,它会在每一行上工作,而对于 session.Query(Model1).all() 中的行, ORM 在开始将它们提供给您之前对所有行进行处理。

无论您选择使用这两个选项中的哪一个,SQLAlchemy 都将始终 ORM 映射所有行。这可以在这些行中的源代码中看到;该loading.instances方法确实会返回一个生成器,但它是已经映射的 ORM 实例之一;您可以在实际的生成器循环代码中确认这一点:

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

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

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

正如上面所解释的,这也是错误的,因为从数据库检索到的所有数据都将在任何屈服完成之前进行处理/映射。

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

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

.all()无论使用与否,都会加载整个结果集。清楚地看到了这一行。这也很容易测试/验证。

我可以从所陈述的 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 映射。

值得一提的是唯一的评论确实是正确并且很容易被忽略(因此写这个答案)指向迈克尔拜耳的解释

于 2019-11-08T15:10:58.527 回答