Query.yield_per()方法与大多数热切加载方案不兼容,包括subqueryload和joinload with collections。
警告
谨慎使用此方法;如果同一个实例存在于多个行中,则最终用户对属性的更改将被覆盖。
特别是,通常不可能将此设置与急切加载的集合(即任何lazy ='joined'或'子查询')一起使用,因为在后续结果批处理中遇到这些集合时将清除新负载。在“子查询”加载的情况下,获取所有行的完整结果,这通常会违背yield_per()的目的。
另请注意,虽然yield_per()会将stream_results执行选项设置为True,但目前只能通过psycopg2 dialect来理解这一点,它将使用服务器端游标流式传输结果,而不是预先缓冲此查询的所有行。其他DBAPI在使所有行可用之前预缓冲所有行。原始数据库行的内存使用远小于ORM映射对象的内存使用,但在进行基准测试时仍应予以考虑。
我真的很难理解yield_per()
的工作原理以及使用此方法的问题究竟是什么。另外,解决这些问题的正确方法是什么,并继续使用此函数迭代大量的行。
我对您拥有的所有建设性信息感兴趣,但这里有一些提示问题:
yield_per()
的查询的一部分。
yield_per()
doc q = sess.query(Object).yield_per(100).options(lazyload('*'), joinedload(Object.some_related))
的示例中,他们停用了lazyload('*')
的eagerload,但保留了一个加入的加载。有没有办法继续使用yield_per()
和eagerload?有什么条件?psycopg2
是唯一支持流结果的DBAPI。这是唯一可以与yield_per()
一起使用的DBAPI吗?据我所知yield_per
使用DBAPI的cursor.fetchmany()
(example)函数支持其中许多函数。据我所知cursor.fetchmany()
支持只获取部分结果并且不提取所有内容(如果它会获取所有内容,为什么函数存在?)yield_per()
完全安全(即使使用eagerload)。这是对的吗?答案 0 :(得分:5)
如果您尝试将这些问题与yield_per
一起使用,那么两个有问题的加载策略都会引发异常,因此您不必太担心。
我相信 subqueryload
唯一的问题是第二个查询的批量加载(尚未实现)。没有什么会在语义上出错,但如果你使用yield_per
,你可能有一个很好的理由不想一次加载所有结果。所以SQLAlchemy礼貌地拒绝违背你的意愿。
joinedload
更加微妙。仅在集合的情况下禁止它,其中主行可能具有多个关联的行。假设您的查询生成这样的原始结果,其中A和B是来自不同表的主键:
A | B
---+---
1 | 1
1 | 2
1 | 3
1 | 4
2 | 5
2 | 6
现在您使用yield_per(3)
获取这些内容。问题是SQLAlchemy只能限制行提取的数量,但它必须返回对象。在这里,SQLAlchemy只能看到前三行,因此它创建了一个{1}}对象,其中包含键1和三个 A
子项:1,2和3。
当它加载下一批时,它想要用键1 ...啊创建一个新的B
对象,但它已经有一个,所以不需要再创建它。额外的A
,4将丢失。 (所以,不,即使使用B
阅读已加入的集合也不安全 - 您的数据块可能会丢失。)
你可能会说“好吧,只要继续阅读行,直到你有一个完整的对象” - 但如果那个yield_per
有一百个孩子怎么办?还是一百万? SQLAlchemy不能合理地保证它可以做你所要求的和产生正确的结果,所以它拒绝尝试。
请记住,DBAPI的设计使得任何数据库可以与相同的API一起使用,即使该数据库不支持所有DBAPI功能也是如此。考虑到DBAPI是围绕游标设计的,但MySQL实际上并没有拥有游标!用于MySQL的DBAPI适配器必须伪造它们。
因此,当A
工作时,您可以从the MySQLdb
source code看到它无法从服务器延迟获取;它将所有内容提取到一个大列表中,然后在您调用cursor.fetchmany(100)
时返回一个切片。
fetchmany
支持的是真正的流式传输,其中结果在服务器上记住持久,并且您的Python进程一次只能看到其中的一些。
您仍然可以将psycopg2
与yield_per
或任何其他DBAPI一起使用;这就是DBAPI设计的重点。您将不得不为DBAPI中隐藏的所有原始行支付内存成本(这些是元组,相当便宜),但您不会 必须为所有ORM对象付费在同一时间。
答案 1 :(得分:-4)
请注意,有时yield_per会失败,不确定根是否为yield_per。 (您可以通过将收益率数字设置为幻数来进行测试,例如51、17)
sqlalchemy.exc.ProgrammingError:(_mysql_exceptions.ProgrammingError)(2014,“命令不同步;您现在不能运行此命令”)
数据库:mysql:5.7.0