首页 / Prisma ORM 入门教程 / 关联查询:嵌套读取、关系过滤与 Fluent API

Prisma ORM 入门教程

关联查询:嵌套读取、关系过滤与 Fluent API

本教程共 54 篇 · 第 24 篇 · 更新于 2026-08-11 · 约 4 分钟阅读

include嵌套写入connectOrCreateFluent APIN+1关系查询

本节目标:学会一次性读多层关系、在嵌套中过滤排序,掌握嵌套写入与 Fluent API。

第 21 章讲了 include 和 select 的基本用法。这一章深入关系:多级嵌套、嵌套里过滤排序、嵌套写入全家族,以及链式查询 Fluent API。

深层嵌套读取

include 可以无限往下层叠:

const user = await prisma.user.findFirst({
  include: {
    posts: {
      include: {
        categories: true,
      },
    },
  },
});

结果里 user → posts → categories 三层结构完整带出。每一层都能继续 include 或 select,比如只取帖子的标题和分类名:

await prisma.user.findFirst({
  select: {
    email: true,
    posts: {
      select: {
        title: true,
        categories: { select: { name: true } },
      },
    },
  },
});

include 与 select 同层互斥,跨层可以混用:include 里嵌 select,select 里也能嵌 include。

Tip

嵌套越深,返回的数据越大。列表页通常只用到两层,第三层以上先想想是否真的需要。

嵌套关系里的过滤与排序

include 的关系可以带 where、orderBy、take:

const user = await prisma.user.findFirst({
  where: { id: 1 },
  include: {
    posts: {
      where: { published: true },
      orderBy: { createdAt: "desc" },
      take: 5,
    },
  },
});

「取某个用户最近 5 篇已发布的帖子」一条查询完成。注意这里的 where 只作用于嵌套的 posts,不影响外层用户。

关系计数

想知道每个用户有几篇文章,不必把帖子全查出来:

await prisma.user.findMany({
  include: {
    _count: {
      select: {
        posts: true,
        comments: true,
      },
    },
  },
});

_count 还能加条件,比如只数已发布的帖子。细节见第 21 章。

嵌套写入:一次查询写多张表

嵌套写(nested writes)在一条查询里操作关联记录,整体是一个事务:任何一步失败,全部回滚。创建用户并带两篇帖子:

await prisma.user.create({
  data: {
    email: "elsa@prisma.io",
    posts: {
      create: [{ title: "第一篇" }, { title: "第二篇" }],
    },
  },
});

写入家族全家福:

操作作用
create / createMany新建关联记录
connect连接已存在的记录(按唯一字段)
connectOrCreate有就连接,没有就创建
disconnect断开连接(一对一用 disconnect: true)
set整体替换关联列表,set: [] 清空
update / updateMany更新关联记录
upsert存在则更新,不存在则创建
deleteMany删除关联记录(可带条件)

举例,给已有用户挂上三篇已有文章:

await prisma.user.update({
  where: { id: 9 },
  data: {
    posts: { connect: [{ id: 8 }, { id: 9 }, { id: 10 }] },
  },
});

connect 时记录不存在会抛错。不确定存不存在就用 connectOrCreate:

author: {
  connectOrCreate: {
    where: { email: "viola@prisma.io" },
    create: { email: "viola@prisma.io", name: "Viola" },
  },
},

批量删除一篇用户的所有草稿:

await prisma.user.update({
  where: { id: 11 },
  data: {
    posts: { deleteMany: { published: false } },
  },
});
Note

嵌套写整体是一个事务,适合「用户 + 订单 + 明细」这类需要同时成功或同时失败的操作。嵌套 createMany 不能再套更深的关系,需要多层时改用嵌套 create 或 $transaction(第 26 章)。

关系过滤:回顾与加深

第 22 章的操作符在嵌套里同样可用,且能继续深入关系的关系:

await prisma.user.findMany({
  where: {
    posts: {
      some: {
        categories: { some: { name: { in: ["Food", "教程"] } } },
      },
    },
  },
});

some / every / none 管一对多,is / isNot 管一对一,深度不限。

Fluent API:链式遍历关系

Fluent API 用方法链代替 include,一层一层往下走:

const posts = await prisma.user
  .findUnique({ where: { email: "alice@prisma.io" } })
  .posts();

const categories = await prisma.post
  .findUnique({ where: { id: 1 } })
  .categories();

链多长都行,最后一步决定返回类型。规则只有一条:上一步必须返回单个对象,所以 findUnique 之后才能继续链,findMany 之后不行。

链式写法让关系遍历代码更简洁;但要防 N+1 得靠 include 或批量 in 查询,不要在循环里逐条发链式查询。循环里逐条查关系是典型的 N+1:

// 坏:1 + N 条查询
const users = await prisma.user.findMany();
for (const user of users) {
  await prisma.post.findMany({ where: { authorId: user.id } });
}

换成 include 一次带齐,或先查出主记录再按 id 批量 in 查询,查询次数就不再随记录数线性增长。

Note

等价场景下,include 嵌套读取会发一条主查询 + 按主表 id 批量查关系表(共 2 条 SQL);Fluent API 一次链式调用拆成两条查询。include 一条调用把关系带齐,适合列表页一次取全;需要按需分批取关系时 Fluent API 更灵活。

参考来源

  • Prisma 官方文档:Relation queries
  • Mapagam:Selecting and Including Data / Working with One-to-Many Relations
  • HireNodeJS:Prisma ORM in Production(N+1 Prevention)