新手快速入门Prisma

181 阅读26分钟

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 ORMMongoDB
Stringstring
Booleanbool
Intint
BigIntlong
Floatdouble
Decimal目前不支持
DateTimetimestamp
BytesbinData
Json

1.3 从 Prisma ORM 到 Mysql 的原生类型映射

Prisma ORMMySQL备注
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-0000:00:00
JsonJSON仅在 MySQL 5.7+ 中受支持
Bytes (字节)LONGBLOB

2. Prisma 模型

模型代表你的应用程序域的实体

映射到数据库中的(关系型数据库如 PostgreSQL)或集合(MongoDB)

2.1 模型字段类型

标量类型描述常见数据库映射示例
String文本数据。PostgreSQL textvarchar(x);
MySQL VARCHAR(x)TEXT;
SQLite TEXT
Boolean真/假值。PostgreSQL boolean;
MySQL TINYINT(1)/BOOLEAN;
SQLite INTEGER (0 或 1)
Int32位有符号整数(范围 ≈ ±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 LONGBLOBBINARY(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' } }