Loading...
Loading...
Compare original and translation side by side
Edit your data contract. Prisma handles the rest.
编辑你的数据契约,Prisma 处理其余工作。
db.transaction(...)countsumavgdb.transaction(...)countsumavgprisma-next-contractdb.tsprisma-next-runtimeprisma-next-debugprisma-next-contractdb.tsprisma-next-runtimeprisma-next-debugdbsrc/prisma/db.ts@prisma-next/<target>/runtimedb.orm.<Model>db.orm.User.where(...).select(...).orderBy(...).all()Contractdb.sql.<table>db.sql.userJOINdb.sql| Need | Choose | Why |
|---|---|---|
| Standard CRUD with relations | ORM ( | Highest ergonomics; fully typed; model-shaped. |
| Eager-load related records | ORM | Composes with |
| Aggregate (count, sum, avg) | ORM | Typed result; works with grouping ( |
| ORM mutations (returns updated rows) or | ORM returns inserted/updated rows; SQL builder exposes |
Computed projection (e.g. | SQL builder ( | The ORM projects model fields; arbitrary expression projection is the SQL builder's seam. |
Complex | SQL builder | The ORM doesn't express arbitrary joins. |
Postgres-specific feature ( | SQL builder, falling back to extension operators when the extension provides them | DSL first; extensions can contribute operators ( |
src/prisma/db.tsdb@prisma-next/<target>/runtimedb.orm.<Model>db.orm.User.where(...).select(...).orderBy(...).all()Contractdb.sql.<table>db.sql.userJOINdb.sql| 需求 | 选择方式 | 原因 |
|---|---|---|
| 带关联关系的标准CRUD操作 | ORM( | 最高的易用性;完全类型校验;基于模型结构。 |
| 预加载关联记录 | ORM | 可与分支上的 |
| 聚合操作(count、sum、avg等) | ORM | 类型化结果;支持分组( |
带类型化结果的 | ORM 变更操作(返回更新后的行)或** | ORM返回插入/更新后的行;SQL构建器显式提供 |
计算投影(例如 | SQL构建器( | ORM仅投影模型字段;任意表达式投影是SQL构建器的核心场景。 |
复杂 | SQL构建器 | ORM无法表达任意连接逻辑。 |
Postgres专属特性( | SQL构建器,当扩展提供相关操作符时可回退到扩展操作符 | 优先使用DSL;扩展可贡献专属操作符(如 |
db.orm.<Model>.all().first().count().aggregate(...)u.field.<op>(value)// src/queries/users.ts — one directory deep under src/, so the import is '../prisma/db'
import { db } from '../prisma/db';
// Find one record by primary key shorthand.
const user = await db.orm.User.first({ id: userId });
// Returns the full row or `null`.
// Find one matching a predicate.
const alice = await db.orm.User
.where((u) => u.email.eq('alice@example.com'))
.first();
// Find many with projection, sort, and limit.
const recentUsers = await db.orm.User
.select('id', 'email', 'createdAt')
.orderBy((u) => u.createdAt.desc())
.take(10)
.all();.where(...)// Lambda form — full expression power.
db.orm.User.where((u) => u.email.eq('alice@example.com'));
// Shorthand object form — equality on the named fields.
db.orm.User.where({ kind: 'admin' });.eq.neq.lt.lte.gt.gte.like.ilike.in([...]).isNull().isNotNull()pgvector.cosineDistance(...)postgis.within(...).intersectsBbox(...).distanceSphere(...)cipherstash.cipherstashEq(...).cipherstashGt(...).between(a, b).where(...)and(...)// Chained .where() — each clause AND-composes with the previous one.
await db.orm.Sale
.where((s) => s.day.gte(start))
.where((s) => s.day.lte(end))
.all();
// Equivalent with an explicit `and(...)` inside one clause.
import { and } from '@prisma-next/sql-orm-client'; // façade re-export pending — see *What PN doesn't do yet*
await db.orm.Sale
.where((s) => and(s.day.gte(start), s.day.lte(end)))
.all();.where()and(...)betweenandornot.some(...).none(...).every(...)@prisma-next/sql-orm-clientimport { and, or, not } from '@prisma-next/sql-orm-client';
await db.orm.User
.where((u) =>
and(
or(u.kind.eq('admin'), u.email.ilike('%@example.com')),
not(u.posts.none((p) => p.title.ilike('%draft%'))),
),
)
.all();.orderBy(...).asc().desc().take(n).skip(n)await db.orm.Post
.where((p) => p.authorId.eq(userId))
.orderBy([(p) => p.createdAt.desc(), (p) => p.id.desc()])
.take(20)
.all();
// Cursor pagination — order by an indexed unique column and filter past the cursor.
const cursor = lastPostFromPreviousPage.createdAt;
await db.orm.Post
.where((p) => p.createdAt.lt(cursor))
.orderBy((p) => p.createdAt.desc())
.take(20)
.all();.first().first({ pk }).all().first()LIMIT 1.first({ pk }).all()LIMITdb.orm.<Model>.all().first().count().aggregate(...)u.field.<op>(value)// src/queries/users.ts — 位于src下一级目录,因此导入路径为'../prisma/db'
import { db } from '../prisma/db';
// 通过主键快速查询单条记录。
const user = await db.orm.User.first({ id: userId });
// 返回完整行数据或`null`。
// 根据条件查询单条记录。
const alice = await db.orm.User
.where((u) => u.email.eq('alice@example.com'))
.first();
// 查询多条记录,指定投影字段、排序和条数限制。
const recentUsers = await db.orm.User
.select('id', 'email', 'createdAt')
.orderBy((u) => u.createdAt.desc())
.take(10)
.all();.where(...)// Lambda形式 — 支持完整表达式能力。
db.orm.User.where((u) => u.email.eq('alice@example.com'));
// 简写对象形式 — 对指定字段进行相等匹配。
db.orm.User.where({ kind: 'admin' });.eq.neq.lt.lte.gt.gte.like.ilike.in([...]).isNull().isNotNull()pgvector.cosineDistance(...)postgis.within(...).intersectsBbox(...).distanceSphere(...)cipherstash.cipherstashEq(...).cipherstashGt(...).between(a, b).where(...)and(...)// 链式.where() — 每个子句与前一个自动AND组合。
await db.orm.Sale
.where((s) => s.day.gte(start))
.where((s) => s.day.lte(end))
.all();
// 等价于在单个子句中显式使用`and(...)`。
import { and } from '@prisma-next/sql-orm-client'; // 门面重导出待实现 — 参见*Prisma Next 当前未支持的功能*
await db.orm.Sale
.where((s) => and(s.day.gte(start), s.day.lte(end)))
.all();.where()and(...)betweenandornot.some(...).none(...).every(...)@prisma-next/sql-orm-clientimport { and, or, not } from '@prisma-next/sql-orm-client';
await db.orm.User
.where((u) =>
and(
or(u.kind.eq('admin'), u.email.ilike('%@example.com')),
not(u.posts.none((p) => p.title.ilike('%draft%'))),
),
)
.all();.orderBy(...).asc().desc().take(n).skip(n)await db.orm.Post
.where((p) => p.authorId.eq(userId))
.orderBy([(p) => p.createdAt.desc(), (p) => p.id.desc()])
.take(20)
.all();
// 游标分页 — 按索引唯一列排序,并过滤掉游标之前的记录。
const cursor = lastPostFromPreviousPage.createdAt;
await db.orm.Post
.where((p) => p.createdAt.lt(cursor))
.orderBy((p) => p.createdAt.desc())
.take(20)
.all();.first().first({ pk }).all().first()LIMIT 1.first({ pk }).all()LIMITawait.toArray()for awaitawait.toArray()for await.all()AsyncIterableResult<Row>PromiseLike<Row[]>AsyncIterable<Row>const users = await db.orm.User.select('id', 'email').all();
// ^? Row[] ← the Thenable resolves to a real array. This is the default idiom.collect()toArray()awaitawaitthen(...)// Explicit buffering — same outcome as `await ... .all()`, useful when you
// want a named Promise<Row[]> to thread through downstream code.
const rows: Promise<User[]> = db.orm.User.select('id', 'email').all().toArray();
// Streaming — process rows one at a time without buffering the whole result.
// Use for genuinely large result sets (anything that wouldn't fit comfortably
// in memory) or pipelines where you can start work before all rows arrive.
for await (const user of db.orm.User.select('id', 'email').all()) {
process(user);
}.first()LIMIT 1const user = await db.orm.User.where({ id }).all().first();
// ^? Row | null ← buffers, returns the first row or null. Issues no LIMIT.
const required = await db.orm.User.where({ id }).all().firstOrThrow();
// ^? Row ← buffers; throws `RUNTIME.NO_ROWS` if empty..first()LIMIT 1.all().first()AsyncIterableResultawait.toArray()for awaitRUNTIME.ITERATOR_CONSUMED// Bad — second await throws RUNTIME.ITERATOR_CONSUMED.
const result = db.orm.User.select('id', 'email').all();
const a = await result;
const b = await result;
// Good — buffer once, reuse the array.
const users = await db.orm.User.select('id', 'email').all();
const a = users;
const b = users;collect(...)toArray(...).all()await.all()AsyncIterableResult<Row>PromiseLike<Row[]>AsyncIterable<Row>const users = await db.orm.User.select('id', 'email').all();
// ^? Row[] ← Thenable对象解析为真实数组。这是默认惯用写法。collect()toArray()awaitawaitthen(...)// 显式缓冲 — 与`await ... .all()`结果相同,适用于需要将Promise<Row[]>传递给下游代码的场景。
const rows: Promise<User[]> = db.orm.User.select('id', 'email').all().toArray();
// 流式处理 — 逐行处理,无需缓冲全部结果。
// 适用于真正的大数据集(无法舒适放入内存)或可在所有行到达前开始处理的流水线场景。
for await (const user of db.orm.User.select('id', 'email').all()) {
process(user);
}.first()LIMIT 1const user = await db.orm.User.where({ id }).all().first();
// ^? Row | null ← 缓冲结果,返回第一行或null。不生成LIMIT。
const required = await db.orm.User.where({ id }).all().firstOrThrow();
// ^? Row ← 缓冲结果;若无数据则抛出`RUNTIME.NO_ROWS`。.first()LIMIT 1.all().first()AsyncIterableResultawait.toArray()for awaitRUNTIME.ITERATOR_CONSUMED// 错误 — 第二次await会抛出RUNTIME.ITERATOR_CONSUMED。
const result = db.orm.User.select('id', 'email').all();
const a = await result;
const b = await result;
// 正确 — 缓冲一次,重用数组。
const users = await db.orm.User.select('id', 'email').all();
const a = users;
const b = users;collect(...)toArray().all()await.include.include.include('<relation>', (branch) => branch.<chain>).where.select.orderBy.takeawait db.orm.User
.select('id', 'email')
.include('posts', (post) =>
post
.select('id', 'title', 'createdAt')
.orderBy((p) => p.createdAt.desc())
.take(5),
)
.take(10)
.all();
// → Array<{ id, email, posts: Array<{ id, title, createdAt }> }>1:N → 1:NUser → posts → commentslateraljsonAggprisma-next-contractprisma-next-queries.include('<relation>', (branch) => branch.<chain>).where.select.orderBy.takeawait db.orm.User
.select('id', 'email')
.include('posts', (post) =>
post
.select('id', 'title', 'createdAt')
.orderBy((p) => p.createdAt.desc())
.take(5),
)
.take(10)
.all();
// → Array<{ id, email, posts: Array<{ id, title, createdAt }> }>1:N → 1:NUser → posts → commentslateraljsonAggprisma-next-contractprisma-next-queries// Create — returns the inserted row.
const user = await db.orm.User.create({ id, email, displayName, kind, createdAt });
// Create with selected return — narrows the return shape.
const summary = await db.orm.User
.select('id', 'email', 'kind')
.create({ id, email, displayName, kind, createdAt });
// Update by predicate.
await db.orm.User.where({ id }).update({ email: newEmail });
// Update with selected return.
await db.orm.User
.where({ id })
.select('id', 'email', 'kind')
.update({ email: newEmail });
// Delete by predicate.
await db.orm.User.where({ id }).delete();
// Upsert — typed by the create branch's shape.
await db.orm.User
.select('id', 'email', 'kind', 'createdAt')
.upsert({
create: { id, email, displayName, kind, createdAt: new Date() },
update: { email, displayName, kind },
});.returning(...)// 创建记录 — 返回插入的行。
const user = await db.orm.User.create({ id, email, displayName, kind, createdAt });
// 创建记录并指定返回字段 — 缩小返回结果结构。
const summary = await db.orm.User
.select('id', 'email', 'kind')
.create({ id, email, displayName, kind, createdAt });
// 根据条件更新记录。
await db.orm.User.where({ id }).update({ email: newEmail });
// 更新记录并指定返回字段。
await db.orm.User
.where({ id })
.select('id', 'email', 'kind')
.update({ email: newEmail });
// 根据条件删除记录。
await db.orm.User.where({ id }).delete();
// 插入更新操作 — 类型由create分支的结构决定。
await db.orm.User
.select('id', 'email', 'kind', 'createdAt')
.upsert({
create: { id, email, displayName, kind, createdAt: new Date() },
update: { email, displayName, kind },
});.returning(...)const totals = await db.orm.User.aggregate((aggregate) => ({
totalUsers: aggregate.count(),
}));
const adminTotals = await db.orm.User
.where({ kind: 'admin' })
.aggregate((aggregate) => ({
adminUsers: aggregate.count(),
}));
// Group-by + aggregate.
const byKind = await db.orm.User
.groupBy('kind')
.having((having) => having.count().gte(minUsers))
.aggregate((aggregate) => ({
totalUsers: aggregate.count(),
}));aggregate.count().sum(field).avg(field).min(field).max(field)| Aggregate | Type | Empty result |
|---|---|---|
| | |
| | |
| | |
| | |
| | |
const revenue = await db.orm.Sale
.where((s) => s.day.gte(start))
.aggregate((a) => ({ total: a.sum('amount') }));
// revenue.total: number | null
const safe = revenue.total ?? 0; // ← apply at the consumption site, not in the aggregate spec.?? 0sumconst totals = await db.orm.User.aggregate((aggregate) => ({
totalUsers: aggregate.count(),
}));
const adminTotals = await db.orm.User
.where({ kind: 'admin' })
.aggregate((aggregate) => ({
adminUsers: aggregate.count(),
}));
// 分组 + 聚合。
const byKind = await db.orm.User
.groupBy('kind')
.having((having) => having.count().gte(minUsers))
.aggregate((aggregate) => ({
totalUsers: aggregate.count(),
}));aggregate.count().sum(field).avg(field).min(field).max(field)| 聚合方法 | 类型 | 空结果时返回值 |
|---|---|---|
| | |
| | |
| | |
| | |
| | |
const revenue = await db.orm.Sale
.where((s) => s.day.gte(start))
.aggregate((a) => ({ total: a.sum('amount') }));
// revenue.total: number | null
const safe = revenue.total ?? 0; // ← 在消费端应用,而非聚合声明中。?? 0sumdb.sql.<table>db.sql.<table>db.sql.<table>db.runtime().execute(plan)JOIN// src/queries/posts.ts — adjust the relative import to match file depth.
import { db } from '../prisma/db';
// Select with predicate and limit.
const plan = db.sql.post
.select('id', 'title', 'userId', 'createdAt')
.where((f, fns) => fns.eq(f.userId, userId))
.limit(limit)
.build();
const rows = await db.runtime().execute(plan);.where(...)(fields, fns)fieldsfnsfns.eqfns.nefns.gtfnsfns.distanceSpherefns.cosineDistancedb.sql.<table>db.runtime().execute(plan)JOIN// src/queries/posts.ts — 根据文件层级调整相对导入路径。
import { db } from '../prisma/db';
// 带条件和条数限制的查询。
const plan = db.sql.post
.select('id', 'title', 'userId', 'createdAt')
.where((f, fns) => fns.eq(f.userId, userId))
.limit(limit)
.build();
const rows = await db.runtime().execute(plan);.where(...)(fields, fns)fieldsfnsfns.eqfns.nefns.gtfnsfns.distanceSpherefns.cosineDistanceINSERTUPDATEDELETERETURNINGRETURNINGINSERTUPDATEDELETE// Insert and return selected columns.
const plan = db.sql.user
.insert({ email })
.returning('id', 'email')
.build();
const [row] = await db.runtime().execute(plan);
// Update with predicate and returning.
const updatePlan = db.sql.user
.update({ email: newEmail })
.where((f, fns) => fns.eq(f.id, userId))
.returning('id', 'email')
.build();
const rows = await db.runtime().execute(updatePlan);
// Delete with predicate.
const deletePlan = db.sql.user
.delete()
.where((f, fns) => fns.eq(f.id, userId))
.build();
await db.runtime().execute(deletePlan);.returning(...)returning// 插入记录并返回指定列。
const plan = db.sql.user
.insert({ email })
.returning('id', 'email')
.build();
const [row] = await db.runtime().execute(plan);
// 根据条件更新记录并返回结果。
const updatePlan = db.sql.user
.update({ email: newEmail })
.where((f, fns) => fns.eq(f.id, userId))
.returning('id', 'email')
.build();
const rows = await db.runtime().execute(updatePlan);
// 根据条件删除记录。
const deletePlan = db.sql.user
.delete()
.where((f, fns) => fns.eq(f.id, userId))
.build();
await db.runtime().execute(deletePlan);.returning(...)returning// Project a computed expression alongside model fields.
const plan = db.sql.cafe
.select('id', 'name')
.select('meters', (f, fns) => fns.distanceSphere(f.location, point))
.orderBy((f, fns) => fns.distanceSphere(f.location, point), { direction: 'asc' })
.orderBy((f) => f.id, { direction: 'asc' })
.limit(limit)
.build();
const rows = await db.runtime().execute(plan);
// Self-join with an alias.
db.sql.post
.innerJoin(db.sql.post.as('p2'), (f, fns) => fns.ne(f.p1.userId, f.p2.userId))
// ...
.build();// 投影计算表达式与模型字段。
const plan = db.sql.cafe
.select('id', 'name')
.select('meters', (f, fns) => fns.distanceSphere(f.location, point))
.orderBy((f, fns) => fns.distanceSphere(f.location, point), { direction: 'asc' })
.orderBy((f) => f.id, { direction: 'asc' })
.limit(limit)
.build();
const rows = await db.runtime().execute(plan);
// 自连接并使用别名。
db.sql.post
.innerJoin(db.sql.post.as('p2'), (f, fns) => fns.ne(f.p1.userId, f.p2.userId))
// ...
.build();db.transaction(fn)txtx.ormtx.sqldb.ormdb.sqltx.execute(plan)await db.transaction(async (tx) => {
const user = await tx.orm.User.create({ id, email });
await tx.orm.Post.create({ userId: user.id, title: 'hello' });
// SQL-builder plan inside the transaction.
const plan = tx.sql.post.update({ status: 'archived' })
.where((f, fns) => fns.lt(f.createdAt, cutoff))
.build();
await tx.execute(plan);
// If anything throws, all three operations roll back.
});db.transaction(...)db.transaction(fn)txtx.ormtx.sqldb.ormdb.sqltx.execute(plan)await db.transaction(async (tx) => {
const user = await tx.orm.User.create({ id, email });
await tx.orm.Post.create({ userId: user.id, title: 'hello' });
// 事务内的SQL构建器计划。
const plan = tx.sql.post.update({ status: 'archived' })
.where((f, fns) => fns.lt(f.createdAt, cutoff))
.build();
await tx.execute(plan);
// 若任何操作抛出错误,所有三个操作都会回滚。
});db.transaction(...)tsx my-script.tsawait db.close()prisma-next-runtimeawait using// src/scripts/seed.ts
import { db } from '../prisma/db';
for (const u of users) {
await db.orm.User.create(u);
}
console.log('Seeded.');
await db.close();tsx my-script.tsawait db.close()prisma-next-runtimeawait using// src/scripts/seed.ts
import { db } from '../prisma/db';
for (const u of users) {
await db.orm.User.create(u);
}
console.log('数据初始化完成。');
await db.close();db.sqldb.orm.all().all()LIMIT.first()LIMIT 1.first({ pk })collect()toArray().all().all()AsyncIterableResult<Row>PromiseLike<Row[]>await collection.all()Row[]AsyncIterableResultRUNTIME.ITERATOR_CONSUMEDcount()?? 0count()numbernumber | null0?? 0sumavgminmaxnumber | null.between(a, b).where((m) => m.field.gte(a)).where((m) => m.field.lte(b))and(m.field.gte(a), m.field.lte(b)).where()andornot@prisma-next/sql-orm-clientdb.sql.from(tables.user)db.sql.<tableName>.select(...)db.schema.tablesdb.execute(plan)db.runtime().execute(plan)tx.execute(plan)capabilities: { includeMany: true }prisma-next.config.tsdefineConfigcapabilitieslateraljsonAggreturningextensions: [...]prisma-next-contractdb.sql.raw(...).stream()db.execute(plan).create.update.delete.first.all.aggregatedb.runtime().execute(...)groupBy(...).aggregate(...).sort().slice().orderBy(...).take(...)db.sql.<table>GROUP BYORDER BYLIMITdb.sql.all().all()LIMIT.first()LIMIT 1.first({ pk })collect()toArray().all().all()AsyncIterableResult<Row>PromiseLike<Row[]>await collection.all()Row[]AsyncIterableResultRUNTIME.ITERATOR_CONSUMED?? 0count()count()numbernumber | null0?? 0sumavgminmaxnumber | null.between(a, b).where((m) => m.field.gte(a)).where((m) => m.field.lte(b)).where()and(m.field.gte(a), m.field.lte(b))andornot@prisma-next/sql-orm-clientdb.sql.from(tables.user)db.sql.<tableName>.select(...)db.schema.tablesdb.execute(plan)db.runtime().execute(plan)tx.execute(plan)prisma-next.config.tscapabilities: { includeMany: true }defineConfigcapabilitieslateraljsonAggreturningextensions: [...]prisma-next-contractdb.sql.raw(...).stream()db.execute(plan).create.update.delete.first.all.aggregatedb.runtime().execute(...)groupBy(...).aggregate(...).sort().slice().orderBy(...).take(...)db.sql.<table>GROUP BYORDER BYLIMITandornot@prisma-next/sql-orm-clientTML-2526@prisma-next/sql-orm-client@prisma-next/postgres/runtimeprisma-next-feedback.orderBy(...).take(...)db.orm.<Model>.groupBy(...).aggregate(...)Promise<Array<Group & Aggregates>>db.sql.<table>GROUP BYORDER BYLIMITprisma-next-feedbackdb.sql.raw(...)prisma-next-feedback.sqldb.runtime().execute(plan).sqlprisma-next-feedbackEXPLAIN.explain()pg.Poolpg:prisma-next-runtimeEXPLAIN ANALYZEprisma-next-feedback.stream().skip(n).take(m)pg.Clientpg:prisma-next-feedbackdb.$transaction([call1, call2])db.transaction(async (tx) => { ... })prisma-next-feedback.include(...).include(...)lintsprisma-next-runtimeWHEREDELETEUPDATELIMITSELECTandornot@prisma-next/sql-orm-clientTML-2526@prisma-next/sql-orm-client@prisma-next/postgres/runtimeprisma-next-feedback.orderBy(...).take(...)db.orm.<Model>.groupBy(...).aggregate(...)Promise<Array<Group & Aggregates>>db.sql.<table>GROUP BYORDER BYLIMITprisma-next-feedbackdb.sql.raw(...)prisma-next-feedback.sqldb.runtime().execute(plan).sqlprisma-next-feedbackEXPLAIN.explain()pg:pg.Poolprisma-next-runtimeEXPLAIN ANALYZEprisma-next-feedback.stream().skip(n).take(m)pg:pg.Clientprisma-next-feedbackdb.$transaction([call1, call2])db.transaction(async (tx) => { ... })prisma-next-feedback.include(...).include(...)lintsprisma-next-runtimeDELETEUPDATEWHERESELECTLIMITexamples/prisma-next-demo/src/orm-client/examples/prisma-next-demo/src/queries/packages/3-extensions/sql-orm-client/src/packages/2-sql/4-lanes/sql-builder/src/examples/prisma-next-demo/src/orm-client/examples/prisma-next-demo/src/queries/packages/3-extensions/sql-orm-client/src/packages/2-sql/4-lanes/sql-builder/src/.first().first({ pk }).all().all()awaitcollect()toArray()for awaitsumavgminmax?? 0count()number.where(...)and(...).between(...)andornot@prisma-next/sql-orm-clientdb.runtime().execute(plan)tx.execute(plan)db.transaction(async (tx) => { ... })db.sql.raw.stream()db.batch.between(...)capabilitiesdefineConfigdb.sql.from(tables.user)prisma-next-feedbackdb.sql.<table>groupBy(...).aggregate(...).first().first({ pk }).all()await.all()collect()toArray()for await?? 0sumavgminmaxcount()number.where()and(...).between(...)@prisma-next/sql-orm-clientandornotdb.runtime().execute(plan)tx.execute(plan)db.transaction(async (tx) => { ... })db.sql.raw.stream()db.batch.between(...)defineConfigcapabilitiesdb.sql.from(tables.user)prisma-next-feedbackdb.sql.<table>groupBy(...).aggregate(...)