我想问一下两者之间有什么区别
for row in session.Query(Model1):
pass
和
for row in session.Query(Model1).all():
pass
是第一个以某种方式用单个查询轰炸数据库的迭代器,而后一个“急切”迭代器以列表的形式查询整个对象(例如range(x)vs xrange(x))?
Answers:
不,数据库流量没有差异。区别在于,for row in session.Query(Model1)
在将ORM分配给您时,它会在每行上工作,而for row in session.Query(Model1).all()
在将ORM分配给您之前,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()
通过它。
实际的区别在于,前者可以在数据到达后立即开始为您提供行,从而将DB结果集“流化”给您,从而减少了内存使用和延迟。我不能肯定地说所有当前的引擎实现都能做到(我希望他们能做到!)。无论如何,后一个版本都没有充分理由阻止这种效率。
q.all()
?
list(q)
我猜比它更具可读性。但是行为和性能是相同的。与迭代相比,迭代q.all()
至少可能比启动慢,启动起来也更耗费内存q
。
all
它将把整个结果集加载到内存中。这可能是千兆字节的数据。
实际上,接受的响应是不正确的(或者至少不再是正确的),特别是以下语句是错误的:
(1)区别仅在于session.Query(Model1)中的行要在将其提供给您时,ORM在每一行上工作,而session.Query(Model1).all()中的行则进行在开始将它们提供给您之前,ORM会在所有行上工作。
无论您选择使用2个选项中的哪一个,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映射。
__iter__
确实预取了所有结果(有充分的理由),但是如果您知道自己在做什么,则可以更改此行为。