从 SQL 数据库中的多个表获取相关数据可能会非常耗费资源。Prisma ORM 现在允许您在数据库级别和应用级别连接之间进行选择,以便您可以为关系查询选择性能最佳的方法。
目录
Prisma ORM 新功能:选择最佳 Join 策略 🎉
支持数据库级别的连接一直是 Prisma ORM 中最受请求的功能之一,我们很高兴分享它现在已作为另一种查询策略提供!
对于任何带有 include
(或 select
)的关系查询,现在在顶层有一个名为 relationLoadStrategy
的新选项。此选项接受两个可能的值之一
join
(默认):使用数据库级别的连接策略在数据库中合并数据。query
:使用应用级别的连接策略,通过向各个表发送多个查询并在应用层中合并数据。
要启用新的 relationLoadStrategy
,您首先需要将预览功能标志添加到 Prisma Client 的 generator
代码块中
注意:
relationLoadStrategy
仅适用于 PostgreSQL 和 MySQL 数据库。
完成之后,您需要重新运行 prisma generate
以使此更改生效,并在您的查询中选择关系加载策略。
这是一个使用新的 join
策略的示例
请注意,由于 "join"
是默认值,因此 relationLoadStrategy
选项在上面的代码片段中实际上也可以省略。我们在这里展示它只是为了说明目的。
join
vs query
— 何时使用哪个?
现在有了这两种查询策略,您可能会想:何时使用哪个?
由于 Prisma ORM 在 PostgreSQL 上使用横向聚合 JOIN,在 MySQL 上使用相关子查询,因此在大多数情况下,join
策略可能更有效(后面的章节将对此进行更详细的介绍)。数据库引擎非常强大,并且擅长优化查询计划。这种新的关系加载策略正是对这一点的致敬。
但是,在某些情况下,您可能仍然希望使用 query
策略,对每个表执行一个查询,并在应用级别合并数据。根据数据集和模式中配置的索引,发送多个查询可能更有效。分析和基准测试您的查询对于识别这些情况至关重要。
另一个需要考虑的因素可能是复杂连接查询造成的数据库负载。如果由于某种原因,数据库服务器上的资源稀缺,您可能希望将复杂的连接查询(带有过滤器和分页)所需的繁重计算转移到您的应用服务器,这可能更容易扩展。
TLDR
- 新的
join
策略在大多数情况下会更有效。 - 在某些边缘情况下,根据数据集和查询的特征,
query
可能会更有效。我们建议您分析您的数据库查询以识别这些情况。 - 如果您想节省数据库服务器上的资源,并将合并和转换数据的繁重工作放在可能更易于扩展的应用服务器中进行,请使用
query
。
理解 SQL 数据库中的关系
现在我们了解了 Prisma ORM 的 JOIN 策略,让我们回顾一下关系查询在 SQL 数据库中的一般工作方式。
关系数据的扁平与嵌套数据结构
SQL 数据库以扁平(即 规范化)的方式存储数据。实体之间的关系通过外键表示,外键指定跨表的引用。
另一方面,应用程序开发人员通常习惯于使用嵌套数据,即可以任意深度嵌套其他对象的对象。
这是一个巨大的差异,不仅体现在数据物理地在磁盘和内存中的布局方式,还体现在心智模型和对数据的推理方面。

关系数据需要为应用程序开发人员“合并”
由于相关数据在数据库中物理上是分开存储的,因此需要在某个地方合并才能成为应用程序开发人员熟悉的嵌套结构。这种合并也称为“连接”。
这种连接可以在两个地方发生
- 在数据库级别:向数据库发送单个 SQL 查询。该查询使用
JOIN
关键字或相关子查询,让数据库执行跨多个表的连接并返回嵌套结构。 - 在应用级别:向数据库发送多个查询。每个查询仅访问单个表,然后查询结果在应用层中的内存中合并。这曾经是
v5.9.0
之前 Prisma Client 支持的唯一查询策略。
哪种方法更可取取决于所使用的数据库、数据集的大小和特征以及查询的复杂性。请继续阅读以了解何时建议使用哪种策略。
底层发生了什么?
Prisma ORM 使用 LATERAL
连接和数据库级别的 JSON 聚合(例如,通过 PostgreSQL 中的 json_agg
)以及 MySQL 上的相关子查询来实现新的 join
关系加载策略。
在以下部分中,我们将研究为什么 PostgreSQL 上的 LATERAL
连接和数据库级别 JSON 聚合方法比普通的传统 JOIN 更有效。
通过 JSON 聚合防止查询结果中的冗余
当使用数据库级别的 JOIN
时,有几种构建 SQL 查询的选项。让我们考虑上面 Prisma 模式的 SQL 表定义
要检索所有用户及其帖子,您可以使用简单的 LEFT JOIN
查询
这是使用一些示例数据的可能结果
请注意在本例中 user_name
列中的冗余。连接的表越多,这种冗余只会变得更糟。例如,假设还有另一个 Comment
表,其中每个评论都有一个 postId
外键,指向 Post
表中的记录。
这是一个表示它的 SQL 查询
现在,假设第一个帖子有多个评论
在这种情况下,结果集的大小随着连接的表数量呈指数增长。由于这些数据通过网络从数据库传输到应用服务器,因此可能会变得非常昂贵。
Prisma 通过数据库级别的 JSON 聚合实现的 join
策略解决了这个问题。
这是一个 PostgreSQL 的示例,它使用 json_agg
和 json_build_object
来解决冗余问题,并以 JSON 格式返回每个用户的帖子
这次的结果集不包含冗余数据。此外,数据结构已经方便地具有 Prisma Client 返回的形状,这节省了在查询引擎中转换结果的额外工作
横向 JOIN 用于更高效的带分页和过滤的查询
关系查询(像大多数其他查询一样)几乎从不从表中获取所有数据,而是带有额外的结果集约束,例如过滤器和分页。特别是分页在传统的 JOIN 中可能变得非常复杂,让我们看另一个例子。
考虑这个 Prisma Client 查询,它获取 10 个用户和每个用户 5 个帖子
当用原始 SQL 编写时,您可能会想在子查询中使用 LIMIT
子句,例如
但是,这行不通,因为内部 SELECT
实际上并没有返回每个用户五个帖子——相反,它返回总共两个帖子,这当然根本不是想要的结果。
使用传统的 JOIN,可以通过使用 row_number()
函数为结果集中的记录分配递增的整数来解决此问题,通过这些整数可以手动执行分页的计算。
然而,这种方法很快变得非常复杂,因此对于构建分页关系查询来说并不理想。
维护、扩展和调试这些类型的 SQL 查询令人望而生畏,并且可能会消耗数小时的开发时间。
值得庆幸的是,较新的数据库版本通过一种新型查询解决了这个问题:横向 JOIN。
上面的查询可以通过使用 LATERAL
关键字来简化
这不仅使查询更具可读性,而且数据库引擎也可能更能够优化查询,因为它能够更多地了解查询的意图。
结论
让我们回顾一下使用 Prisma 连接关系查询数据的不同选项。
过去,Prisma 仅支持应用级别的连接策略,该策略向数据库发送多个查询,并在查询引擎内部完成将数据合并和转换为预期 JavaScript 对象结构的所有工作

使用普通的传统 JOIN,数据的合并将委托给数据库。但是,如上所述,数据冗余(结果集随着关系查询中表数量的增加呈指数增长)以及包含过滤器和分页的查询的复杂性存在问题

为了解决这些问题,Prisma ORM 实现了现代的横向 JOIN,并结合了数据库级别的 JSON 聚合。这样,解决查询并将数据引入预期的 JavaScript 对象结构所需的所有繁重工作都在数据库级别完成
尝试一下并分享您的反馈
我们很乐意您尝试新的关系查询加载策略。请告诉我们您的想法,并与我们分享您的反馈!
不要错过下一篇文章!
注册 Prisma 新闻简报