You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Blog refresh: Choosing the Best Join Strategy in Prisma ORM (#8016)
Reframe from 2024 announcement to evergreen guide with answer-first
intro. Setup updated to prisma-client generator with driver adapter and
Prisma Postgres. New wire-level section shows the actual SQL for both
strategies, captured on Prisma 7.8.0. Corrects the CREATE TABLE block
to match what the schema really generates (NOT NULL + RESTRICT). Adds
Joins in Prisma Next section with behavior verified on the 0.14.0 Early
Access build: single query with json_agg, no flag needed. Keeps
relationJoins Preview framing because the feature never went GA. Slug,
author, and publish date unchanged; updatedAt bumped.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Ankur Datta <64993082+ankur-arch@users.noreply.github.com>
metaTitle: "Prisma ORM Now Lets You Choose the Best Join Strategy (Preview)"
8
-
metaDescription: "Choose between DB-level and application-level joins to pick the most performant approach for your relation queries."
8
+
metaTitle: "Choosing the Best Join Strategy in Prisma ORM: join vs query"
9
+
metaDescription: "Prisma ORM loads relations with database-level joins (a single SQL query with JSON aggregation) or application-level joins (one query per table). Learn how both work and when to use which."
Fetching related data from multiple tables in SQL databases can get expensive. Prisma ORM now lets you choose between _database-level_and _application-level joins_ so that you can pick the most performant approach for your relation queries.
17
+
Prisma ORM supports two strategies for loading related data. With the `join` strategy, the database merges the relations itself and returns nested data from a single SQL query, using JSON aggregation and lateral joins on PostgreSQL. With the `query` strategy, Prisma sends one query per table and merges the results in the application. You pick the strategy per query with the `relationLoadStrategy` option, available on PostgreSQL, CockroachDB, and MySQL behind the `relationJoins` preview feature flag; once the flag is on, `join` is the default. This post explains how both strategies work under the hood and when to use which.
17
18
18
19
## Contents
19
20
20
-
-[New in Prisma ORM: Choose the best Join strategy 🎉](#new-in-prisma-orm-choose-the-best-join-strategy-)
21
-
-[`join` vs `query` — when to use which?](#join-vs-query--when-to-use-which)
21
+
-[The two join strategies in Prisma ORM](#the-two-join-strategies-in-prisma-orm)
22
+
-[What actually goes over the wire](#what-actually-goes-over-the-wire)
23
+
-[`join` vs `query`: when to use which?](#join-vs-query-when-to-use-which)
22
24
-[Understanding relations in SQL databases](#understanding-relations-in-sql-databases)
23
25
-[What's happening under the hood?](#whats-happening-under-the-hood)
26
+
-[Joins in Prisma Next](#joins-in-prisma-next)
24
27
-[Try it out and share your feedback](#try-it-out-and-share-your-feedback)
25
28
26
-
## New in Prisma ORM: Choose the best join strategy 🎉
29
+
## The two join strategies in Prisma ORM
27
30
28
-
[Support for database-level joins](https://github.com/prisma/prisma/issues/5184)has been one of the most requested features in Prisma ORM and we're excited to share that it's now available as another query strategy!
31
+
[Support for database-level joins](https://github.com/prisma/prisma/issues/5184)was one of the most requested features in Prisma ORM, and it shipped as the `relationLoadStrategy` option.
For any relation query with `include` (or `select`), there is now a new option on the top-level called `relationLoadStrategy`. This option accepts one out of two possible values:
34
+
For any relation query with `include` (or `select`), the top-level `relationLoadStrategy`option accepts one of two values:
32
35
33
36
-`join` (default): Uses the database-level join strategy to merge the data in the database.
34
37
-`query`: Uses the application-level join strategy by sending multiple queries to individual tables and merging the data in the application layer.
35
38
36
-
To enable the new `relationLoadStrategy`, you'll first need to add the preview feature flag to the `generator` block of your Prisma Client:
39
+
To enable `relationLoadStrategy`, add the `relationJoins`preview feature flag to the `generator` block of your Prisma schema:
37
40
38
41
```prisma
42
+
// prisma/schema.prisma
39
43
generator client {
40
-
provider = "prisma-client-js"
44
+
provider = "prisma-client"
45
+
output = "../src/generated/prisma"
41
46
previewFeatures = ["relationJoins"]
42
47
}
43
-
```
44
-
> **Note**: The `relationLoadStrategy` is only available for PostgreSQL and MySQL databases.
45
48
46
-
Once that's done, you'll need to re-run `prisma generate` for this change to take effect and pick a relation load strategy in your queries.
49
+
datasource db {
50
+
provider = "postgresql"
51
+
}
52
+
```
47
53
48
-
Here is an example that uses the new `join` strategy:
54
+
> **Note**: `relationLoadStrategy`is available for PostgreSQL, CockroachDB, and MySQL databases. Without the flag, Prisma Client always uses the application-level strategy, and the `relationLoadStrategy` option is rejected as an unknown argument.
49
55
56
+
You'll need a PostgreSQL database to follow along. The fastest way to get one is [Prisma Postgres](https://www.prisma.io/postgres): running `npm create db` provisions a database and gives you the connection string to put in your `.env` file.
50
57
58
+
After adding the flag, re-run `npx prisma generate` for the change to take effect. Here is the data model we'll use, along with the client setup and an example query using the `join` strategy:
Because `"join"` is the default once the preview flag is enabled, the `relationLoadStrategy` option could technically also be omitted in the snippet above. We show it here for illustration purposes.
91
+
92
+
## What actually goes over the wire
93
+
94
+
The difference between the two strategies is easy to see in the SQL that Prisma Client generates. We ran the query above on Prisma ORM 7.8 against a PostgreSQL database and logged the statements.
75
95
76
-
Note that because `"join"` is the default, the `relationLoadStrategy` option could technically also be omitted in the code snippet above. We just show it here for illustration purposes.
96
+
With the `query` strategy (or without the preview flag), fetching users with their posts sends two queries, one per table:
Prisma Client then merges the two result sets in memory to build the nested objects.
79
104
80
-
Now with these two query strategies, you'll wonder: When to use which?
105
+
With the `join` strategy, the same Prisma Client query sends exactly one statement, in which the database performs a lateral join and builds the nested JSON structures itself:
81
106
82
-
Because of the lateral, aggregated JOINs that Prisma ORM uses on PostgreSQL and the correlated subqueries on MySQL, the `join` strategy is likely to be more efficient in the majority of cases (a [later section](#whats-happening-under-the-hoods) will have more details on this). Database engines are very powerful and great at optimizing query plans. This new relation load strategy pays tribute to that.
FROM (...) -- posts of the current user, shaped as JSON
113
+
) AS"User_posts"ON TRUE
114
+
```
83
115
84
-
However, there may be cases where you may still want to use the `query` strategy to perform one query per table and merge data at the application-level. Depending on the dataset and the indexes that are configured in the schema, sending multiple queries could be more performant. Profiling and benchmarking your queries will be crucial to identify these situations.
116
+
One round trip, no in-memory merging, and the JSON aggregation happens where the data lives.
85
117
86
-
Another consideration could be the database load that's incurred by a complex join query. If, for some reason, resources on the database server are scarce, you may want to move the heavy compute that's required by a complex join query with filters and pagination to your application servers which may be easier to scale.
118
+
## `join` vs `query`: when to use which?
119
+
120
+
With two query strategies available, you'll wonder: when to use which?
121
+
122
+
Because of the lateral, aggregated JOINs that Prisma ORM uses on PostgreSQL and the correlated subqueries on MySQL, the `join` strategy is likely to be more efficient in the majority of cases (a [later section](#whats-happening-under-the-hood) has more details on this). Database engines are very powerful and great at optimizing query plans. The `join` relation load strategy pays tribute to that.
123
+
124
+
However, there may be cases where you still want to use the `query` strategy to perform one query per table and merge data at the application level. Depending on the dataset and the indexes that are configured in the schema, sending multiple queries could be more performant. Profiling and benchmarking your queries is crucial to identify these situations.
125
+
126
+
Another consideration could be the database load that's incurred by a complex join query. If, for some reason, resources on the database server are scarce, you may want to move the heavy compute that's required by a complex join query with filters and pagination to your application servers, which may be easier to scale.
87
127
88
128
TLDR:
89
129
90
-
- The new `join` strategy will be more efficient in most scenarios.
130
+
- The `join` strategy will be more efficient in most scenarios.
91
131
- There may be edge cases where `query` could be more performant depending on the characteristics of the dataset and query. We recommend that you profile your database queries to identify these scenarios.
92
-
- Use `query` if you want to save resources on the database server and do heavy-lifting of merging and transforming data in the application server which might be easier to scale.
132
+
- Use `query` if you want to save resources on the database server and do the heavylifting of merging and transforming data in the application server, which might be easier to scale.
93
133
94
134
## Understanding relations in SQL databases
95
135
96
-
Now that we learned about Prisma ORM's JOIN strategies, let's review how relation queries generally work in SQL databases.
136
+
Now that we learned about Prisma ORM's join strategies, let's review how relation queries generally work in SQL databases.
97
137
98
138
### Flat vs nested data structures for relations
99
139
@@ -112,13 +152,13 @@ Since related data is stored physically separately in the database, it needs to
112
152
There are two places where this join can happen:
113
153
114
154
- On the **database-level**: A single SQL query is sent to the database. The query uses the `JOIN` keyword or a correlated subquery to let the database perform the join across multiple tables and returns the nested structures.
115
-
- On the **application-level**: Multiple queries are sent to the database. Each query only accesses a single table and the query results are then merged in-memory in the application layer. This used to be the only query strategy that Prisma Client supported before `v5.9.0`.
155
+
- On the **application-level**: Multiple queries are sent to the database. Each query only accesses a single table and the query results are then merged in-memory in the application layer. This was the only query strategy that Prisma Client supported before v5.9.0, and it remains the default behavior when the `relationJoins` preview flag is not enabled.
116
156
117
157
Which approach is more desirable depends on the database that's used, the size and characteristics of the dataset, and the complexity of the query. Read on to learn when it's recommended to use which strategy.
118
158
119
159
## What's happening under the hood?
120
160
121
-
Prisma ORM implements the new `join` relation load strategy using `LATERAL` joins and DB-level JSON aggregation (e.g. via `json_agg`) in PostgreSQL and correlated subqueries on MySQL.
161
+
Prisma ORM implements the `join` relation load strategy using `LATERAL` joins and DB-level JSON aggregation (e.g. via `json_agg`) in PostgreSQL and correlated subqueries on MySQL.
122
162
123
163
In the following sections, we'll investigate why the `LATERAL` joins and DB-level JSON aggregation approach on PostgreSQL is more efficient than plain, traditional JOINs.
124
164
@@ -139,13 +179,13 @@ CREATE TABLE "User" (
139
179
CREATETABLE "Post" (
140
180
"id"SERIALNOT NULL,
141
181
"title"TEXTNOT NULL,
142
-
"authorId"INTEGER,
182
+
"authorId"INTEGERNOT NULL,
143
183
144
184
CONSTRAINT"Post_pkey"PRIMARY KEY ("id")
145
185
);
146
186
147
187
-- AddForeignKey
148
-
ALTERTABLE"Post" ADD CONSTRAINT"Post_authorId_fkey"FOREIGN KEY ("authorId") REFERENCES"User"("id") ON DELETESETNULLONUPDATE CASCADE;
188
+
ALTERTABLE"Post" ADD CONSTRAINT"Post_authorId_fkey"FOREIGN KEY ("authorId") REFERENCES"User"("id") ON DELETERESTRICTONUPDATE CASCADE;
149
189
```
150
190
To retrieve all users with their posts, you can use a simple `LEFT JOIN` query:
151
191
@@ -217,7 +257,7 @@ GROUP BY
217
257
ORDER BY
218
258
u.id;
219
259
```
220
-
The result set this time doesn't contain redundant data. Additionally, the data structure conveniently already has the shape that's returned by Prisma Client which saves the extra work of transforming results in the query engine:
260
+
The result set this time doesn't contain redundant data. Additionally, the data structure conveniently already has the shape that's returned by Prisma Client, which saves the extra work of transforming results in the client:
However, this won't work because the inner `SELECT` doesn't actually return five posts _per user_ — instead it returns two posts _in total_ which is of course not at all the desired outcome.
293
+
However, this won't work because the inner `SELECT` doesn't actually return five posts _per user_. Instead, it returns at most five posts _in total_, which is of course not at all the desired outcome.
254
294
255
295
Using a traditional JOIN, this could be resolved by using the `row_number()` function to assign incrementing integers to the records in the result set with which the computation of the pagination could be performed manually.
256
296
@@ -295,7 +335,7 @@ This not only makes the query more readable, but the database engine also likely
295
335
296
336
Let's review the different options for joining data from relation queries with Prisma.
297
337
298
-
In the past, Prisma only supported the application-level join strategy which sends multiple queries to the database and does all the work of merging and transforming it into the expected JavaScript object structures inside of the query engine:
338
+
With the application-level join strategy (`query`, and the behavior before v5.9.0), Prisma Client sends multiple queries to the database and does all the work of merging and transforming the results into the expected JavaScript object structures inside the client:
[Prisma Next](https://www.prisma.io/blog/prisma-next-early-access-write-your-contract-prompt-your-agent-ship-your-app), the next generation of Prisma ORM (available in Early Access today, becoming Prisma 8 at GA), takes the idea in this post one step further: you no longer choose a join strategy at all.
314
355
315
-
We'd love for you to try out to the new loading strategy for relation queries. Let us know what you think and [share your feedback with us](https://github.com/prisma/prisma/discussions/22288)!
356
+
In Prisma Next, the join strategy is selected from your database target's declared capabilities. We verified this on the current Early Access build: `db.orm.public.User.include("posts").all()` sends a single SQL query in which PostgreSQL builds the nested JSON with `json_agg` and `json_build_object`, with no preview flag and no per-query option. On targets without JSON aggregation support, Prisma Next falls back to multiple queries with in-application merging, so the same application code picks the best available strategy per database.
357
+
358
+
Prisma Next is also fast in general: in [our published benchmark](https://www.prisma.io/blog/prisma-next-performance-benchmark), a fork of the open-source drizzle-benchmarks suite, it reaches roughly 90% of the raw `pg` driver's speed, holds p95 latency around 4 ms at 6,000 to 7,000 requests/second, and ships a client of about 148.5 KB gzipped. And when you need full control over a join, it includes a type-safe SQL query builder with explicit `leftJoin` support, kept in sync with your schema.
359
+
360
+
Like Prisma ORM, Prisma Next pairs with [Prisma Postgres](https://www.prisma.io/postgres) out of the box. For new projects, especially ones built with AI coding agents, it's the direction to watch.
361
+
362
+
## Try it out and share your feedback
316
363
364
+
We'd love for you to try out the `join` relation load strategy. If you don't have a database at hand, `npm create db` gives you a free Prisma Postgres database to experiment with. Let us know what you think and [share your feedback with us](https://github.com/prisma/prisma/discussions/22288)!
0 commit comments