1.1 Prisma ORM 连接数据库
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
1.2 从 Prisma ORM 到 MongoDB 的原生类型映射
| Prisma ORM | MongoDB |
|---|---|
String | string |
Boolean | bool |
Int | int |
BigInt | long |
Float | double |
Decimal | 目前不支持 |
DateTime | timestamp |
Bytes | binData |
Json |
1.3 从 Prisma ORM 到 Mysql 的原生类型映射
| Prisma ORM | MySQL | 备注 |
|---|---|---|
String (字符串) | VARCHAR(191) | |
Boolean (布尔) | BOOLEAN | 在 MySQL 中,BOOLEAN 是 TINYINT(1) 的同义词 |
Int (整数) | INT | |
BigInt (大整数) | BIGINT | |
Float (浮点数) | DOUBLE | |
Decimal (十进制) | DECIMAL(65,30) | |
DateTime (日期时间) | DATETIME(3) | 目前,Prisma ORM 不支持 MySQL 中的零日期(0000-00-00, 00:00:00) |
Json | JSON | 仅在 MySQL 5.7+ 中受支持 |
Bytes (字节) | LONGBLOB |
2. Prisma 模型
模型代表你的应用程序域的实体
映射到数据库中的表(关系型数据库如 PostgreSQL)或集合(MongoDB)
2.1 模型字段类型
| 标量类型 | 描述 | 常见数据库映射示例 |
|---|---|---|
String | 文本数据。 | PostgreSQL text, varchar(x);MySQL VARCHAR(x), TEXT;SQLite TEXT |
Boolean | 真/假值。 | PostgreSQL boolean; MySQL TINYINT(1)/BOOLEAN; SQLite INTEGER (0 或 1) |
Int | 32位有符号整数(范围 ≈ ±21亿)。 | PostgreSQL integer;MySQL INT; SQLite INTEGER |
BigInt | 大整数,用于超过 Int 范围的整数(如 ID、金融计算)。 | PostgreSQL bigint; MySQL BIGINT;SQLite INTEGER |
Float | 双精度浮点数(IEEE 754)。不适用于精确值(如货币) ,有精度限制。 | PostgreSQL double precision; MySQL DOUBLE;SQLite REAL |
Decimal | 高精度十进制数。适用于精确值(如货币、科学计算) ,无精度损失。 | PostgreSQL numeric(x, y), decimal(x, y);MySQL DECIMAL(x, y); SQLite TEXT |
DateTime | 日期和时间(无时区信息)。Prisma 在 API 层返回 ISO 8601 格式字符串。 | PostgreSQL timestamp(3); MySQL DATETIME(3);SQLite NUMERIC |
Json | 存储 JSON 数据。Prisma 在 API 层返回 JavaScript 对象/数组/值。 | PostgreSQL jsonb; MySQL JSON; SQLite TEXT |
Bytes | 存储二进制数据(如文件、图片)。 | PostgreSQL bytea;MySQL LONGBLOB, BINARY(x), VARBINARY(x); SQLite BLOB |
Unsupported | 表示 Prisma 暂不直接支持的类型。需自行处理数据库特定类型(如枚举、几何)。 | 取决于数据库 |
2.2 模型字段属性
2.2.1 @id ( 表示 指定该字段为模型的主键 )
id Int @id @default(autoincrement())
2.2.2 @default(value) ( 表示 设置字段的默认值。值可以是静态值 【 如字符串、数字 】 或函数 【 如now()、autoincrement()、uuid() 】 )。
isActive Boolean @default(true) // 布尔值
role String @default("USER") // 字符串
score Int @default(100) // 数字
createdAt DateTime @default(now()) // 当前时间
token String @default(uuid()) // UUID
2.2.3 @unique ( 表示 确保该字段的值在表中唯一 【可以用于非主键字段】 )
2.2.4 @relation ( 表示 用于定义关系字段 【关联其他模型】。 必需参数:fields【当前模型的字段】和 references【关联模型的字段】 )
author User @relation(fields: [authorId], references: [id])
authorId Int
2.2.5 @map(name) ( 表示 将字段映射到数据库中具有不同名称的列 )
model Comment {
id String @default(auto()) @map("_id") @db.ObjectId
@@map("comments")
}
2.2.6 @updatedAt ( 自动更新字段为当前时间戳 【仅适用于DateTime字段】 )。
updatedAt DateTime @updatedAt
2.2.7 @db.Type ( 指定数据库中原生的列类型 【例如@db.VarChar(255)、@db.Int等】 )。
// PostgreSQL
name String @db.VarChar(100)
price Decimal @db.Numeric(10,2)
// MySQL
content String @db.Text
isActive Boolean @db.TinyInt(1)
// SQLite
data Json @db.JSON
2.2.8 @ignore ( 【在Prisma内部使用,不常见】标记字段在Prisma Client中忽略,通常用于迁移过程中 ) 。
model Post {
id Int @default(autoincrement()) // no unique identifier
author User @relation(fields: [authorId], references: [id])
authorId Int
email String @ignore // 此字段将被排除
@@ignore // 这个Post 模型将从 Prisma Client 中排除
}
2.2.9 块属性
| 属性 | 描述 |
|---|---|
@@id | 定义复合主键 |
@@unique | 定义复合唯一约束 |
@@index | 创建数据库索引 |
@@map | 自定义数据库表名 |
@@schema | 指定数据库schema |
@@ignore | 将 @@ignore 添加到你希望从 Prisma Client 中排除的模型 |
model Product {
sku String
category String
price Decimal
// 复合主键
@@id([sku, category])
// 复合唯一约束
@@unique(fields: [sku, category])
// 创建价格索引
@@index([price], name: "price_index")
// 自定义表名
@@map("products_table")
@@ignore
}
2.3 模型中属性函数
2.3.1 auto() ( 表示由数据库自动生成的默认值 )。
此函数仅在 MongoDB 上可用
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
}
2.3.2 autoincrement() ( 表示在底层数据库中创建整数序列,并根据该序列将递增的值分配给所创建记录的 ID 值 )
MongoDB 不支持,该函数
// 生成自增整数作为 ID(仅限关系型数据库)
model User {
id Int @id @default(autoincrement())
name String
}
2.3.3 sequence() ( 表示 在底层数据库中创建整数序列,并根据该序列将递增的值分配给所创建记录的值 )
| 可选参数 | 示例 |
| ----------- | ------------------------------------------------------------------------------------ |
| `virtual` | `@default(sequence(virtual))` 虚拟序列是不生成单调递增值的序列,而是生成类似于内置函数 `unique_rowid()` 生成的值。 |
| `cache` | `@default(sequence(cache: 20))` 要在内存中缓存以供会话重用的序列值数量。缓存大小为 `1` 表示没有缓存,小于 `1` 的缓存大小无效。 |
| `increment` | `@default(sequence(increment: 4))` 序列递增的新值。负数创建降序序列。正数创建升序序列。 |
| `minValue` | `@default(sequence(minValue: 10))` 序列的新最小值。 |
| `maxValue` | `@default(sequence(maxValue: 3030303))` 序列的新最大值。 |
| `start` | `@default(sequence(start: 2))` 序列开始的值,如果它重新启动或序列达到 `maxValue`。
2.3.4 cuid() ( 表示 根据 cuid规范生成全局唯一标识符 )
如果你想使用 cuid2值,你可以将 2 作为参数传递给 cuid 函数:cuid(2)
model User {
id String @id @default(cuid(2))
name String
}
2.3.5 uuid() ( 表示 根据 UUID 规范生成全局唯一标识符。Prisma ORM 支持版本 4(默认)和 7 )
model User {
id String @id @default(uuid(7))
name String
}
2.3.6 ulid() 表示 根据 ULID 规范生成全局唯一的、可按字典序排序的标识符。(将生成 128 位随机标识符,表示为 26 个字符长的字母数字字符串,例如:01ARZ3NDEKTSV4RRFFQ69G5FAV)
model User {
id String @id @default(ulid())
name String
}
2.3.7 nanoid() ( 表示 根据 Nano ID 规范生成值。nanoid() 接受一个介于 2 到 255 之间的整数值,该值指定生成 ID 值的长度, 无值则默认值为 21 )
model User {
id String @id @default(nanoid(16))
name String
}
2.3.8 now() ( 表示 设置记录创建时的时间戳 )
model User {
id String @id
createdAt DateTime @default(now())
}
2.3.9 dbgenerated(...) ( 表示 Prisma schema 中无法表达的默认值 )
model User {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
id String @id @default(uuid()) @db.Uuid
test String?
}
2.4 模型中关系字段
关系是 Prisma schema 中两个模型之间的连接
model Post {
id Int @id @default(autoincrement())
// Other fields
comments Comment[] // A post can have many comments
}
model Comment {
id Int
// Other fields
post Post? @relation(fields: [postId], references: [id]) // A comment can have one post
postId Int?
}
2.5 模型 增删改查
2.5.1 findMany({option}) ( 表示 查询多条记录 【支持分页、过滤、排序】 )
选项
| 名称 | 描述 |
|---|---|
select | 指定返回对象中要包含的属性。 |
include | 指定返回对象中应预加载的关系。 |
omit | 指定要从返回对象中排除的属性。自 5.13.0 版本起进入预览阶段 |
relationLoadStrategy | 默认值:join。指定关联查询的加载策略。仅与 include(或关联字段上的 select)结合使用。自 5.9.0 版本起进入预览阶段。 |
where | 将所有模型字段包装在类型中,以便可以通过任何属性筛选列表。 |
orderBy | 允许您按任何属性对返回列表进行排序。 |
cursor | 指定列表的位置(值通常指定 id 或其他唯一值)。 |
take | 指定列表中应返回的对象数量(从列表的开始(正值)或结束(负值)或从指定的 cursor 位置开始计算) |
skip | 指定列表中应跳过的返回对象数量。 |
distinct | 允许您按特定字段过滤掉重复行 - 例如,仅返回不重复的 Post 标题。 |
const activeUsers = await prisma.user.findMany({
where: {
status: 'ACTIVE',
points: { gt: 100 }
},
orderBy: [
{ points: 'desc' },
{ name: 'asc' }
]
})
2.5.2 findFirst() ( 表示 返回列表中符合您条件的第一个记录 )
const c = await prisma.post.findFirst({
where: {
title: {
startsWith: 'A test', // title 字段开头是 A test
},
},
orderBy: {
title: 'asc', // 排序
},
take: -1, // 翻转
});
2.5.3 findFirstOrThrow() ( 表示 以与 findFirst() 相同的方式检索单个数据记录。但是,如果查询未找到记录,它会抛出 PrismaClientKnownRequestError。 )
2.5.4 findUnique() ( 表示 通过主键或唯一约束字段查询单个记录,注意: 必须使用唯一标识字段(@id 或 @unique 标记的字段) )
model User {
firstName String
lastName String
@@unique(fields: [firstName, lastName], name: "fullname")
}
const result = await prisma.user.findUnique({
where: {
fullname: {
// name property of @@unique attribute - default is firstname_lastname
firstName: 'Alice',
lastName: 'Smith',
},
},
});
2.5.5 findUniqueOrThrow() ( 表示 与 findUnique() 相同的方式检索单个记录。但是,如果查询未找到请求的记录,它会抛出 PrismaClientKnownRequestError )
2.5.6 create() ( 表示 创建新的数据库记录 )
选项
| 名称 | 描述 |
|---|---|
data | 将所有模型字段包装在类型中,以便在创建新记录时提供它们。它还包括关系字段,允许您执行(事务性)嵌套插入。在数据模型中标记为可选或具有默认值的字段是可选的。 |
select | 指定返回对象中要包含的属性。 |
include | 指定返回对象中应预加载的关系。 |
omit | 指定要从返回对象中排除的属性。自 5.13.0 版本起进入预览阶段 |
relationLoadStrategy | 默认值:join。指定关联查询的加载策略。仅与 include(或关联字段上的 select)结合使用。自 5.9.0 版本起进入预览阶段。 |
// 创建新文章并关联到已存在的用户
await prisma.post.create({
data: {
title: "Prisma指南",
author: {
connect: { id: 1 } // 通过ID连接用户
}
}
})
连接多个关系(多对多),可以通过connect对关联的,(也就是可以将两个关联数据库,通过connect进行同时添加数据)如下:
model Post {
id Int @id @default(autoincrement())
tags Tag[] @relation("PostTags")
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[] @relation("PostTags")
}
// 为文章添加多个标签
await prisma.post.create({
data: {
title: "关系数据库",
tags: {
connect: [
{ name: "数据库" }, // 通过唯一字段连接
{ name: "教程" }
]
}
}
})
2.5.7 update() ( 表示 更新现有数据库记录 )
选项
| 名称 | 描述 |
|---|---|
data | 将模型的所有字段包装在类型中,以便在更新现有记录时提供它们。在数据模型中标记为可选或具有默认值的字段是可选的。 |
where | 包装模型的所有字段,以便可以选择记录(了解更多)。 在 4.5.0 版本之前,此类型仅包装模型的唯一字段。 |
select | 指定返回对象中要包含的属性。 |
include | 指定返回对象中应预加载的关系。 |
omit | 指定要从返回对象中排除的属性。自 5.13.0 版本起进入预览阶段。 |
relationLoadStrategy | 默认值:join。指定关联查询的加载策略。仅与 include(或关联字段上的 select)结合使用。自 5.9.0 版本起进入预览阶段。 |
例子
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
// 更新用户并为其创建一篇新帖子
const userWithNewPost = await prisma.user.update({
where: { email: 'sakura@example.com' },
data: {
posts: {
create: {
title: '我的第一篇 Prisma 帖子',
content: '这是通过嵌套写入创建的。',
},
},
},
include: {
posts: true, // 在返回结果中包含帖子信息
},
});
// 更新用户并同时更新其所有帖子的发布状态
const updatedUser = await prisma.user.update({
where: { id: 1 },
data: {
posts: {
updateMany: {
where: {
published: false,
},
data: {
published: true,
},
},
},
},
});
// 更新帖子的标题并更改其作者
const updatedPost = await prisma.post.update({
where: { id: 1 },
data: {
title: '更新后的标题',
author: {
connect: { email: 'yuki@example.com' }, // 将作者连接到 yuki
},
},
});
注意: 要在更新时执行算术运算(加、减、乘、除),请使用原子数字运算以防止竞态条件。
2.5.8 原子数字运算 ( 表示 更新时的原子操作适用于数字字段类型(Float 和 Int)。此功能允许您根据字段的当前值(例如减去或除以)更新字段,而不会冒竞态条件的风险。)
| 运算符 | 作用 | 示例 |
|---|---|---|
increment | 增加指定数值 | { views: { increment: 1 } } |
decrement | 减少指定数值 | { stock: { decrement: 5 } } |
multiply | 乘以指定数值 | { price: { multiply: 0.9 } } |
divide | 除以指定数值 | { points: { divide: 2 } } |
set | 直接设置新值 | { balance: 100 }(非原子操作) |
注意: 每次查询,每个字段只能执行一个原子更新(因为,数据库无法确定执行顺序)
// ❌ 非法:对 price 同时使用两个原子操作,因为数据库无法确定执行顺序
await prisma.item.update({
where: { id: 1 },
data: {
price: {
multiply: 0.9, // 错误:与 increment 冲突
increment: 5 // 错误:与 multiply 冲突
}
}
});
// ✅ 合法:不同字段可各自使用原子操作
await prisma.user.update({
where: { id: 1 },
data: {
points: { increment: 5 }, // 字段A:原子+5
credits: { decrement: 2 }, // 字段B:原子-2
}
});
2.5.9 upsert() 表示如下:
1、 如果现有数据库记录满足 where 条件,则更新该记录
2、 如果没有数据库记录满足 where 条件,则创建新的数据库记录
选项
| 名称 | 描述 |
|---|---|
create | 将模型的所有字段包装在类型中,以便在创建新记录时提供它们。它还包括关系字段,允许您执行(事务性)嵌套插入。在数据模型中标记为可选或具有默认值的字段是可选的。 |
update | 将模型的所有字段包装在类型中,以便在更新现有记录时提供它们。在数据模型中标记为可选或具有默认值的字段是可选的。 |
where | 包装模型的所有字段,以便可以选择记录(了解更多)。 在 4.5.0 版本之前,此类型仅包装模型的唯一字段。 |
select | 指定返回对象中要包含的属性 |
include | 指定返回对象中应预加载的关系。 |
omit | 指定要从返回对象中排除的属性。自 5.13.0 版本起进入预览阶段 |
relationLoadStrategy | 默认值:join。指定关联查询的加载策略。仅与 include(或关联字段上的 select)结合使用。自 5.9.0 版本起进入预览阶段。 |
model User {
id Int @id
profileViews Int
userName String @unique
email String
@@unique([id, profileViews])
}
prisma.user.upsert({
where: {
userName: 'Alice',
},
create: {
id: 1,
profileViews: 1,
userName: 'Alice',
email: 'alice@prisma.io',
},
update: {
email: 'updated@example.com',
},
});
解决 竞态条件
如果同时发生多个 upsert 操作且记录尚不存在,则一个或多个操作可能会返回唯一键约束错误。
解决方案: 在您的应用程序代码中处理 P2002 错误。当它发生时,重试 upsert 操作以更新该行
2.5.10 upsert
如果可能,Prisma Client 会将 upsert 查询交给数据库处理。这称为数据库 upsert。
数据库 upsert 具有以下优点:
1、 它们比 Prisma Client 处理的 upsert 更快
2、 唯一键约束错误不会发生
3、当满足特定条件时,Prisma Client 会自动使用数据库 upsert。当不满足这些条件时,Prisma Client 会处理 upsert。
数据库 upsert 查询条件:
当 upsert 查询满足以下条件时,Prisma Client 会使用数据库 upsert
1、 upsert 的 create 和 update 选项中没有嵌套查询
2、 查询不包含使用嵌套读取的选择
3、 查询仅修改一个模型
4、 upsert 的 where 选项中只有一个唯一字段
5、 where 选项中的唯一字段与 create 选项中的唯一字段具有相同的值
如果您的查询不满足这些条件,则 Prisma Client 会自行处理 upsert。
model User {
id Int @id
profileViews Int
userName String @unique
email String
@@unique([id, profileViews])
}
// 以下 `upsert` 查询满足所有条件,因此 Prisma Client 使用数据库 upsert
prisma.user.upsert({
where: {
userName: 'Alice',
},
create: {
id: 1,
profileViews: 1,
userName: 'Alice',
email: 'alice@prisma.io',
},
update: {
email: 'updated@example.com',
},
});
2.5.11 delete() ( 表示 删除现有数据库记录。您可以删除记录 )
选项
| 名称 | 描述 |
|---|---|
where | 包装模型的所有字段,以便可以选择记录(了解更多)。 在 4.5.0 版本之前,此类型仅包装模型的唯一字段。 |
select | 指定返回对象中要包含的属性。 |
include | 指定返回对象中应预加载的关系。 |
omit | 指定要从返回对象中排除的属性。自 5.13.0 版本起进入预览阶段 |
relationLoadStrategy | 默认值:join。指定关联查询的加载策略。仅与 include(或关联字段上的 select)结合使用。自 5.9.0 版本起进入预览阶段。 |
// 以下查询删除特定用户记录,并使用 `select` 返回已删除用户的 `name` 和 `email`
const deleteUser = await prisma.user.delete({
where: {
email: 'elsa@prisma.io',
},
select: {
email: true,
name: true,
},
});
2.5.12 createMany() ( 表示 创建多条记录。 )
选项
| 名称 | 描述 |
|---|---|
data | 将所有模型字段包装在类型中,以便在创建新记录时提供它们。在数据模型中标记为可选或具有默认值的字段是可选的。 |
skipDuplicates? | 不插入具有已存在唯一字段或 ID 字段的记录。仅受支持 ON CONFLICT DO NOTHING 的数据库支持。这不包括 MongoDB 和 SQLServer |
// 创建多个新用户
const users = await prisma.user.createMany({
data: [
{ name: 'Sonali', email: 'sonali@prisma.io' },
{ name: 'Alex', email: 'alex@prisma.io' },
],
});
2.5.13 createManyAndReturn() ( 表示 创建多条记录并返回结果对象 )
选项
| 名称 | 描述 |
|---|---|
data | 将所有模型字段包装在类型中,以便在创建新记录时提供它们。在数据模型中标记为可选或具有默认值的字段是可选的。 |
select | 指定要在返回对象中包含的属性。 |
omit | 指定要从返回对象中排除的属性。自 5.13.0 版本起进入预览阶段。与 select 互斥。 |
include | 指定在返回对象中应预加载哪些关系。 |
skipDuplicates? | 不插入具有已存在唯一字段或 ID 字段的记录。仅受支持 ON CONFLICT DO NOTHING 的数据库支持。这不包括 MongoDB 和 SQLServer |
const users = await prisma.user.createManyAndReturn({
data: [
{ name: 'Sonali', email: 'sonali@prisma.io' },
{ name: 'Alex', email: 'alex@prisma.io' },
],
})
2.5.14 updateMany() ( 表示 批量更新现有数据库记录并返回更新的记录数量 )
选项
| 名称 | 描述 |
|---|---|
data | 将模型的所有字段包装在类型中,以便在更新现有记录时提供它们。在数据模型中标记为可选或具有默认值的字段在 data 上是可选的。 |
where | 将模型的所有字段包装在类型中,以便可以通过任何属性过滤列表。如果您不过滤列表,则所有记录都将被更新。 |
limit | 限制更新的记录数量。 |
// 更新所有 `email` 包含 `prisma.io` 且至少一篇相关 `Post` 点赞数超过 10 的 `User` 记录
const updatedUserCount = await prisma.user.updateMany({
where: {
email: {
contains: 'prisma.io',
},
posts: {
some: {
likes: {
gt: 10,
},
},
},
},
data: {
role: 'USER',
},
});
2.5.15 updateManyAndReturn() ( 表示 更新多条记录并返回结果对象 )
选项
| 名称 | 描述 |
|---|---|
data | 将模型的所有字段包装在类型中,以便在更新现有记录时提供它们。在数据模型中标记为可选或具有默认值的字段在 data 上是可选的。 |
where | 将模型的所有字段包装在类型中,以便可以通过任何属性过滤列表。如果您不过滤列表,则所有记录都将被更新。 |
const users = await prisma.user.updateManyAndReturn({
where: {
email: {
contains: 'prisma.io',
}
},
data: {
role: 'ADMIN'
},
})
2.5.16 deleteMany() ( 表示 在一个事务中删除多条记录 )
选项
| 名称 | 描述 |
|---|---|
where | 将模型的所有字段包装在类型中,以便可以通过任何字段进行筛选。 |
limit | 限制删除的记录数量。 |
// 删除所有 `email` 包含 `prisma.io` 的 `User` 记录,但限制删除 5 条记录
const deletedUserCount = await prisma.user.deleteMany({
where: {
email: {
contains: 'prisma.io',
},
},
limit: 5,
});
2.5.17 count() ( 表示 专门用于高效计算记录数量的方法【如计算总页数、替换findFirst 提高性能 等等】 )
选项
| 名称 | 描述 |
|---|---|
where | 将所有模型字段包装在类型中,以便可以通过任何属性筛选列表 |
orderBy | 允许您按任何属性对返回列表进行排序。 |
cursor | 指定列表的位置(值通常指定 id 或其他唯一值)。 |
take | 指定列表中应返回的对象数量(从列表的开始(正值)或结束(负值)或从指定的 cursor 位置开始计算) |
skip | 指定列表中应跳过的返回对象数量。 |
// 正确:条件计数
await prisma.user.count({ where: { role: 'ADMIN' } }); // ✅ 返回管理员数量
// 正确:关联计数(通过 findUnique)
await prisma.user.findUnique({
where: { id: 1 },
select: {
_count: { select: { posts: true } } // ✅ 统计该用户的文章数
}
});
2.5.18 aggregate() ( 表示 用于执行数据聚合操作的核心功能,它允许您对数据进行统计计算和高级分析,而不仅仅是获取原始记录 )
选项
| 名称 | 描述 |
|---|---|
where | 将所有模型字段包装在类型中,以便可以通过任何属性筛选列表。 |
orderBy | 允许您按任何属性对返回列表进行排序。 |
cursor | 指定列表的位置(值通常指定 id 或其他唯一值)。 |
take | 指定列表中应返回的对象数量(从列表的开始(正值)或结束(负值)或从指定的 cursor 位置开始计算) |
skip | 指定列表中应跳过的返回对象数量。 |
_count | 返回匹配记录或非 null 字段的计数。 |
_avg | 返回指定字段所有值的平均值。 |
_sum | 返回指定字段所有值的总和。 |
_min | 返回指定字段的最小可用值。 |
_max | 返回指定字段的最大可用值。 |
// 生成销售报告:总销售额、平均订单值、最大订单
const salesReport = await prisma.order.aggregate({
where: {
createdAt: {
gte: new Date('2023-01-01'),
lt: new Date('2024-01-01')
}
},
_sum: { total: true },
_avg: { total: true },
_max: { total: true },
_count: true
});
/*
结果: {
_sum: { total: 125000 },
_avg: { total: 245.09 },
_max: { total: 2999.99 },
_count: 510
}
*/
2.5.19 groupBy() ( 表示 最强大的数据聚合方法之一,它允许您根据指定字段对数据进行分组,并对每个分组执行聚合计算。这是数据分析、报表生成和业务洞察的核心工具 )
选项
| 名称 | 描述 |
|---|---|
where | 将所有模型字段包装在类型中,以便可以通过任何属性筛选列表。 |
orderBy | 允许您根据 by 中也存在的任何属性对返回列表进行排序。 |
by | 指定用于分组记录的字段或字段组合。 |
having | 允许您按聚合值过滤组 - 例如,仅返回平均年龄小于 50 的组。 |
take | 指定列表中应返回的对象数量(从列表的开始(正值)或结束(负值)或从指定的 cursor 位置开始计算) |
skip | 指定列表中应跳过的返回对象数量。 |
_count | 返回匹配记录或非 null 字段的计数。 |
_avg | 返回指定字段所有值的平均值。 |
_sum | 返回指定字段所有值的总和。 |
_min | 返回指定字段的最小可用值。 |
_max | 返回指定字段的最大可用值。 |
const result = await prisma.model.groupBy({
by: ['groupField1', 'groupField2'], // 分组字段
where: { /* 过滤条件 */ }, // 可选的全局过滤
having: { /* 分组后过滤 */ }, // 对分组结果的过滤
orderBy: { /* 排序规则 */ }, // 结果排序
_count: { /* 计数操作 */ }, // 聚合函数
_sum: { /* 求和操作 */ },
// ...其他聚合函数
})
const groupUsers = await prisma.user.groupBy({
by: ['country', 'city'],
_count: {
_all: true,
city: true,
},
_sum: {
profileViews: true,
},
orderBy: {
country: 'desc',
},
having: {
profileViews: {
_avg: {
gt: 200,
},
},
},
});
/** 结果
[
{
country: 'Denmark',
city: 'Copenhagen',
_sum: { profileViews: 490 },
_count: {
_all: 70,
city: 8,
},
},
{
country: 'Sweden',
city: 'Stockholm',
_sum: { profileViews: 500 },
_count: {
_all: 50,
city: 3,
},
},
];
*/
2.6 模型查询选项
2.6.1 where ( 表示 一个或多个过滤器,可用于筛选记录属性 )
where: {
email: { contains: '@gmail.com' }, // 模糊匹配
age: { gt: 18, lte: 30 }, // 范围查询
OR: [ // 逻辑组合
{ role: 'ADMIN' },
{ isVerified: true }
],
posts: { some: { published: true } } // 关系过滤
}
2.6.2 select ( 表示 返回的对象中精确控制返回哪些字段 )
// 只选择用户的姓名和邮箱
const user = await prisma.user.findUnique({
where: { id: 1 },
select: {
name: true,
email: true
}
});
/*
结果: { name: 'Alice', email: 'alice@example.com' }
*/
// 选择用户及其文章标题
const userWithPosts = await prisma.user.findUnique({
where: { id: 1 },
select: {
name: true,
posts: { // 选择关系字段
select: {
title: true
}
}
}
});
2.6.3 include ( 表示 加载关联模型数据 )
// 关系
model User {
id Int @id @default(autoincrement())
name String
posts Post[] // 一对多关系
profile Profile? // 一对一关系
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int
}
model Profile {
id Int @id @default(autoincrement())
bio String
user User @relation(fields: [userId], references: [id])
userId Int
}
// 获取用户及其关联的所有文章
const userWithPosts = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: true // 加载关联的posts
}
});
/*
结果:
{
id: 1,
name: 'Alice',
posts: [
{ id: 1, title: 'First Post', authorId: 1 },
{ id: 2, title: 'Second Post', authorId: 1 }
]
}
// 多级嵌套关联 用户 → 文章 → 评论 → 评论作者
const userWithCommentsAuthors = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: {
include: {
comments: {
include: {
author: true // 加载评论的作者
}
}
}
}
}
});
2.6.4 omit ( 表示 返回的对象中排除哪些字段 )
const results = await prisma.user.findMany({
omit: {
password: true,
},
include: {
posts: {
omit: {
title: true,
},
},
},
});
2.6.5 orderby ( 表示 对记录列表进行排序 )
sort 参数的输入
| 名称 | 描述 |
|---|---|
asc | 升序排序(A → Z) |
desc | 降序排序(Z → A) |
nulls 参数的输入
| 名称 | 描述 |
|---|---|
first | 将 null 值排在最前。 |
last | 将 null 值排在最后。 |
// 按关联的 `User` 记录的 `name` 排序 `Post`,`null` 记录优先
const posts = await prisma.post.findMany({
orderBy: {
author: {
name: { sort: 'asc', nulls: 'first' },
},
},
});
// 排序参数的顺序很重要 - 以下查询按 `role` 排序,然后按 `email` 排序。注意结果差异
const users = await prisma.user.findMany({
select: {
email: true,
role: true,
},
orderBy: [
{
role: 'desc',
},
{
email: 'desc',
},
],
});
2.6.5 distinct ( 表示 从 findMany 或 findFirst 中去除记录列表的重复项 )
// 基础例子
const distinctCities = await prisma.user.findMany({
select: {
city: true,
country: true,
},
distinct: ['city'],
});
注意: Prisma 的 distinct 是基于 数据库行 来进行去重的,而非依据返回的字段,所有下面数据会返回如此奇怪数据内容
const distinctCitiesAndCountries = await prisma.user.findMany({
select: {
city: true,
country: true,
},
distinct: ['city', 'country'],
});
/** 结果
[
{ city: 'Paris', country: 'France' },
{ city: 'Paris', country: 'Denmark' },
{ city: 'Lyon', country: 'France' },
];
*/
2.7 嵌套查询
2.7.1 create
const user = await prisma.user.create({
data: {
email: 'alice@prisma.io',
posts: {
create: [
{ title: 'This is my first post', },
{ title: 'Here comes a second post', },
],
},
},
});
const user = await prisma.user.update({
where: { email: 'alice@prisma.io' },
data: {
posts: {
create: { title: 'Hello World' },
},
},
})
2.7.2 createMany
const user = await prisma.user.update({
where: {
id: 9,
},
data: {
name: 'Elliott',
posts: {
createMany: {
data: [
{ title: 'My first post' },
{ title: 'My second post' }
],
},
},
},
});
2.7.3 set ( 表示 覆盖关系的当前值 — 例如,用不同的列表替换 Post 记录列表 )
// 通过断开任何先前的 `Post` 记录并连接两条其他现有记录来更新现有 `User` 记录
const user = await prisma.user.update({
where: { email: 'alice@prisma.io' },
data: {
posts: {
set: [
{ id: 32 }, { id: 42 }
],
},
},
});
2.7.4 connect ( 表示 嵌套 connect 查询通过指定 ID 或唯一标识符将记录连接到现有相关记录 )
// 创建新的 `Profile` 记录并通过唯一字段将其连接到现有 `User` 记录
const user = await prisma.profile.create({
data: {
bio: 'Hello World',
user: {
connect: { email: 'alice@prisma.io' },
},
},
});
// 创建新的 `Profile` 记录并通过 ID 字段将其连接到现有 `User` 记录
const user = await prisma.profile.create({
data: {
bio: 'Hello World',
user: {
connect: { id: 42 }, // sets userId of Profile record
},
},
});
2.7.5 connectOrCreate ( 表示 用于在创建或更新记录时智能处理关联关系。它结合了 "连接已有记录" 和 "创建新记录" 两种行为,特别适合处理可能存在或需要新建的关联数据。)
// 创建用户,如果profile不存在则新建
const user = await prisma.user.create({
data: {
email: 'alice@example.com',
profile: {
connectOrCreate: {
// 1. 尝试查找现有profile
where: { userId: 123 }, // 基于唯一字段查找
// 2. 不存在则创建
create: {
bio: 'New user bio'
}
}
}
}
})
三种关系操作对比
| 操作 | 行为 | 适用场景 |
|---|---|---|
| connect | 仅连接已存在记录 | 确定关联记录已存在 |
| create | 始终创建新记录 | 需要全新关联记录 |
| connectOrCreate | 智能连接或创建 | 不确定关联记录是否存在 |
2.7.6 disconnect ( 表示 嵌套 disconnect 查询会中断父记录和相关记录之间的连接,但不会删除任何记录 )
// 断开用户ID=1与其文章ID=5的关联
await prisma.user.update({
where: { id: 1 },
data: {
posts: {
disconnect: [{ id: 5 }] // 断开指定文章
}
}
})
// 从文章ID=3移除所有标签关联
await prisma.post.update({
where: { id: 3 },
data: {
tags: {
disconnect: true // 断开所有关联
}
}
})
// 从文章ID=7断开特定标签
await prisma.post.update({
where: { id: 7 },
data: {
tags: {
disconnect: [
{ id: 10 }, // 断开标签ID=10
{ id: 15 } // 断开标签ID=15
]
}
}
})
2.7.6 update ( 表示 嵌套的 update 查询会更新一个或多个相关记录,其中父记录的 ID 为 n )
// 通过更新所连接的 `Profile` 记录来更新现有 `User` 记录
const user = await prisma.user.update({
where: { email: 'alice@prisma.io' },
data: {
profile: {
update: { bio: 'Hello World' },
},
},
});
2.7.7 upsert ( 表示 嵌套的 upsert 查询会在相关记录存在时更新它,或者在不存在时创建新的相关记录 )
// 通过更新现有 `User` 记录所连接的两个 `Post` 记录或创建新记录来更新现有 `User` 记录(*upsert*)
const user = await prisma.user.update({
where: { email: 'alice@prisma.io' },
data: {
posts: {
upsert: [
{
create: { title: 'This is my first post' },
update: { title: 'This is my first post' },
where: { id: 32 },
},
{
create: { title: 'This is my second post' },
update: { title: 'This is my second post' },
where: { id: 23 },
},
],
},
},
});
2.7.8 delete ( 表示 嵌套的 delete 查询会删除相关记录。父记录不会被删除 )
// 通过删除现有 `User` 记录所连接的两个 `Post` 记录来更新现有 `User` 记录
const user = await prisma.user.update({
where: { email: 'alice@prisma.io' },
data: {
posts: {
delete: [{ id: 34 }, { id: 36 }],
},
},
});
2.7.9 updateMany ( 表示 )
const result = await prisma.user.update({
where: {
id: 2,
},
data: {
posts: {
updateMany: {
where: {
published: false,
},
data: {
likes: 0,
},
},
},
},
});
2.7.10 deleteMany ( 表示 )
const result = await prisma.user.update({
where: {
id: 2,
},
data: {
name: 'Updated name',
posts: {
deleteMany: {},
},
},
});
2.8 筛选条件和运算符
以下是 Prisma ORM 中常用的筛选条件(Filters)和运算符(Operators)的完整整理,包含描述和示例:
一、基本运算符
2.8.1. 等于 equals / =
- 描述:精确匹配值
- 示例:
// SQL: WHERE name = 'Alice' where: { name: 'Alice' } // 显式写法 where: { name: { equals: 'Alice' } }
2.8.2. 不等于 not
- 描述:排除匹配值
- 示例:
// SQL: WHERE name != 'Alice' where: { name: { not: 'Alice' } }
2.8.3. 在列表中 in
- 描述:匹配给定数组中的任意值
- 示例:
// SQL: WHERE role IN ('ADMIN', 'USER') where: { role: { in: ['ADMIN', 'USER'] } }
2.8.4. 不在列表中 notIn
- 描述:排除给定数组中的值
- 示例:
// SQL: WHERE role NOT IN ('GUEST') where: { role: { notIn: ['GUEST'] } }
二、数值/日期比较
2.8.5. 小于 lt
- 描述:字段小于指定值
- 示例:
// SQL: WHERE age < 30 where: { age: { lt: 30 } }
2.8.6. 小于等于 lte
- 描述:字段小于等于指定值
- 示例:
// SQL: WHERE age <= 30 where: { age: { lte: 30 } }
2.8.7. 大于 gt
- 描述:字段大于指定值
- 示例:
// SQL: WHERE age > 20 where: { age: { gt: 20 } }
2.8.8. 大于等于 gte
- 描述:字段大于等于指定值
- 示例:
// SQL: WHERE age >= 18 where: { age: { gte: 18 } }
三、字符串操作
2.8.9. 包含 contains
- 描述:字符串包含子串(区分大小写)
- 示例:
// SQL: WHERE email LIKE '%@gmail.com%' where: { email: { contains: '@gmail.com' } }
2.8.10. 开头匹配 startsWith
- 描述:字符串以指定子串开头
- 示例:
```javascript
// SQL: WHERE name LIKE 'Ali%'
where: { name: { startsWith: 'Ali' } }
```
2.8.11. 结尾匹配 endsWith
- 描述:字符串以指定子串结尾
- 示例:
```javascript
// SQL: WHERE email LIKE '%.com'
where: { email: { endsWith: '.com' } }
```
2.8.12. 模式匹配 mode (搭配字符串操作)
- 描述:控制大小写敏感度
- 示例:
```javascript
// 不区分大小写的搜索
where: {
name: {
contains: 'alice',
mode: 'insensitive' // 默认 'default'(敏感)
}
}
```
四、逻辑组合
2.8.13. 与 AND
- 描述:同时满足多个条件
- 示例:
```javascript
// SQL: WHERE age >= 18 AND role = 'USER'
where: {
AND: [
{ age: { gte: 18 } },
{ role: 'USER' }
]
}
```
2.8.14. 或 OR
- 描述:满足任意一个条件
- 示例:
```javascript
// SQL: WHERE role = 'ADMIN' OR role = 'MODERATOR'
where: {
OR: [
{ role: 'ADMIN' },
{ role: 'MODERATOR' }
]
}
```
2.8.15. 非 NOT
- 描述:排除指定条件
- 示例:
```javascript
// SQL: WHERE NOT (name = 'Alice')
where: { NOT: { name: 'Alice' } }
```
五、空值检查
2.8.16. 为空 isNull
- 描述:字段值为 `null`
- 示例:
```javascript
// SQL: WHERE email IS NULL
where: { email: { isNull: true } }
```
2.8.17. 非空 notNull
- 描述:字段值不为 `null`
- 示例:
```javascript
// SQL: WHERE email IS NOT NULL
where: { email: { notNull: true } }
```
六、关系查询
2.8.18. 存在关联 is / isNot
- 描述:检查关联记录是否存在
- 示例:
```javascript
// 查询有 Profile 的用户
where: { profile: { is: {} } }
// 查询没有 Profile 的用户
where: { profile: { isNot: {} } }
```
2.8.19. 关联筛选 some / every / none
- 描述:针对一对多/多对多关联
- 示例:
```javascript
// 查询有至少一篇已发布文章的用户
where: { posts: { some: { published: true } } }
// 查询所有文章都已发布的用户
where: { posts: { every: { published: true } } }
// 查询没有未发布文章的用户
where: { posts: { none: { published: false } } }
```
七、JSON 字段操作(需数据库支持)
2.8.20. JSON 路径筛选
- 描述:查询 JSON 字段中的值
- 示例:
```javascript
// 查询 metadata 中 theme 为 dark 的记录
where: {
metadata: {
path: ['theme'],
equals: 'dark'
}
}
```
八、其他
2.8.21. 搜索全文索引 search(PostgreSQL 特有)
- 描述:使用全文搜索
- 示例:
```javascript
// 在 content 字段中搜索关键词
where: { content: { search: 'database OR prisma' } }
```
2.8.22. 正则匹配 regex(数据库依赖)
- 描述:使用正则表达式匹配
- 示例:
```javascript
// 匹配以 A 开头的名字(PostgreSQL)
where: { name: { regex: '^A' } }
```
使用说明
-
组合嵌套:所有运算符可多层嵌套
where: { OR: [ { age: { gt: 30 } }, { AND: [ { role: 'ADMIN' }, { email: { contains: 'company.com' } } ] } ] } -
隐式简写:
{ name: 'Alice' }等价于{ name: { equals: 'Alice' } }