如何在 peewee 中预取?

Posted

技术标签:

【中文标题】如何在 peewee 中预取?【英文标题】:How can I prefetch in peewee? 【发布时间】:2019-10-29 12:50:55 【问题描述】:

我正在尝试进行优化的 SQL 查询,以检索对象列表 A 具有对象列表 B 具有对象列表 C 和 D 以及与 E 的多对多关系

我尝试使用聚合行来解决问题,但是当没有 B、C 或 D 类型的对象时我遇到了问题。

例如我试过这个查询:

aa = A.alias()
query = (A.select(A, B, aa, C, D, E).join(B, peewee.JOIN_LEFT_OUTER).join(aa, peewee.JOIN_LEFT_OUTER).switch(B).join(C, peewee.JOIN_LEFT_OUTER).switch(B).join(D, peewee.JOIN.LEFT_OUTER).switch(B).join(E, peewee.JOIN_LEFT_OUTER))

然后,我使用 aggregate_rows 删除重复数据。它在有 B、C 和 D 行时有效,但当没有时,我没有得到相应的 A 对象,其中 B 或 C 或 D 为空列表。

我也尝试过使用预取,但是当我访问时,A.B peewee 不运行查询(好),但是,当我访问 A.B.C peewee 时运行查询(坏)。

A_objects = peewee.prefetch(A,B,C,D,E)

我迷路了,我不知道自己做错了什么。

【问题讨论】:

【参考方案1】:

天哪,这有很多关系。您是否分析过您的代码,并且您确定预取会显着缩短加载时间?您的示例中的最外层循环 (A) 是一个非常大的列表吗?我建议在尝试“优化”您的查询之前先深入研究一下。

aggregate_rows()

听起来您使用的是 Peewee 2.x?这是很旧的,并且是无法维护的。这个方法在 3.x 中被移除了,因为它在 99% 的情况下效率很低。

和E的多对多关系

这很可能是问题所在。多对多实际上是 2 个表——一个带有两个外键的联结表,一个用于源对象,一个用于目标对象,在多对多中相关。因此,如果您想尝试跨多对多预取,您将需要包含联结表。这可能会非常复杂。

您可以在测试中看到一些如何跨多对多预取的示例:

https://github.com/coleifer/peewee/blob/7bfe65cd3689e7bbd0ad5345c0454cba1b559b94/tests/manytomany.py#L278-L308

【讨论】:

是的,我使用的是 2.x,但我会尝试更新它。它是一个嵌入式系统,我们必须仔细测试每一个变化。 我们遇到的性能问题如下。我们正在使用 marshmallow-peewee,并且我们有一个带有 meta=peewee-A-model 的模式。就条目而言,上下文如下:A 类型的 8 个条目,每个条目有 250 个 B 类型,每个 B 有一个 C 和一个 D,每个 B 有大约 5 个 D。 当我们尝试执行 SchemaA.dump(query) 时,问题就出现了。如果查询是 A.select() 类型,它将在尝试访问 A 的不同元素时生成大量查询,例如 A.B.我曾尝试使用 aggregate_rows 进行 JOIN 查询,但正如您之前提到的,不建议这样做,并且它的行为是不明确的。我不知道该怎么做。它在 RPI 上执行,查询需要 54 秒,这是不可用的。我完全迷路了。 我不熟悉“marshmallow”可能在做什么,但我建议当您谈论 5+ 连接时,最好放弃魔法。我怀疑很难以高性能的方式编写查询并将其转储到字典中。 我已经回答了下面的问题,你能告诉我们它是否正确吗?【参考方案2】:

最后,我找到了解决方案。 正如 coleifer 告诉我的以及我在文档中阅读的那样,进行而不是 aggregate_rows 的方法是预取。 在提出问题之前我正在工作的版本是 2.x。该版本使用后缀 _prefetch 获取预取数据。例如,如果 A 有属性 A.something,当我们预取它时,它会在 A.something_prefetch 中恢复。 Marshmallow 直接与我们的模型对话。这意味着,当它想要获取 A.something 时,它将查询 A.something 而不是 A.something_prefetch。 A.peewee 2.x 中的某些东西是一个 SelectQuery 对象,因此它将进行 SQL 查询而不是恢复预取的数据。

最后,我已将 peewee 的版本升级到 3.x,现在它可以工作了。我不知道 coleifer 是否可以告诉我们这个答案是否正确,但现在它正在工作。

希望对你有帮助!

【讨论】:

请务必记录您的查询,以确保您不会意外引入任何低效率,但对我来说听起来是正确的。

以上是关于如何在 peewee 中预取?的主要内容,如果未能解决你的问题,请参考以下文章

教义 orm:绕过延迟加载并在 getter 中预取相关记录

实现搜索的不同方式

核心数据预取和 KVO 合规性

peewee.DataError:字符串或blob太大,如何增加peewee中的`DSQLITE_MAX_VARIABLE_NUMBER`?

如何在 peewee 中使用 backref

如何在peewee中选择虚拟列?