Prisma 中文文档 下载 App

关联查询:include、select 嵌套与关系写入

include 一次查出关联

传统 ORM 最头疼的问题之一是 N+1 查询——查 100 个用户,每个用户单独查他的文章,变成 101 条 SQL。Prisma 的 include 让你一次把关联数据全拉回来:

const users = await prisma.user.findMany({
  include: {
    posts: {
      where: { published: true },
      orderBy: { createdAt: 'desc' },
      take: 5,
    },
    _count: { posts: true },
  },
})

这条查询翻译成大约 3~4 条 SQL(主表查 User、一次 JOIN 查每个 User 的 post 数、一次 JOIN 查每个 User 的前 5 篇已发表文章),而不是 N+1 条。

嵌套 include

include 可以无限嵌套:

const post = await prisma.post.findUnique({
  where: { id: 1 },
  include: {
    author: {
      include: {
        posts: { take: 3 },
      },
    },
    comments: {
      include: { author: true },
    },
  },
})

select 里也能 include 关系

await prisma.user.findMany({
  select: {
    email: true,
    posts: {
      select: { title: true },
    },
  },
})

嵌套写入

Prisma 的杀手级功能——一次查询里同时写关系两边的数据。

create 时嵌套创建关联

await prisma.user.create({
  data: {
    email: 'alice@prisma.io',
    posts: {
      create: [
        { title: '第一篇文章' },
        { title: '第二篇文章' },
      ],
    },
  },
})

一条事务里创建了 1 个 User + 2 个 Post,自动填好外键。

connect 已有记录

如果关联的记录已经存在,用 connect 把它们连起来:

// 先创建 User
const user = await prisma.user.create({ data: { email: 'a@b.com' } })

// 再创建 Post 并 connect 已有的 User
await prisma.post.create({
  data: {
    title: '新文章',
    author: { connect: { id: user.id } },
  },
})

// 或者反过来:创建 Post 时同时 connect 已有的 User
await prisma.post.create({
  data: {
    title: '新文章',
    author: { connect: { email: 'a@b.com' } },  // connect by @unique 字段
  },
})

createOrConnect

找不到就创建,找到了就 connect:

await prisma.post.create({
  data: {
    title: '新文章',
    author: {
      connectOrCreate: {
        where: { email: 'a@b.com' },
        create: { email: 'a@b.com', name: 'A' },
      },
    },
  },
})

多对多的 set / disconnect

// 把 Post 和指定 Tag 关联(替换原来的关联)
await prisma.post.update({
  where: { id: 1 },
  data: {
    tags: {
      set: [{ id: 1 }, { id: 2 }],  // 完全替换关联
    },
  },
})

// 解除关联(不删除 Tag)
await prisma.post.update({
  where: { id: 1 },
  data: {
    tags: {
      disconnect: [{ id: 3 }],
    },
  },
})

深层更新(深层部分更新)

Prisma Client v5.12+ 支持"深层部分更新"——只更新特定关系下的部分字段:

// 给 id=1 的 Post,更新它的某个 comment
await prisma.post.update({
  where: { id: 1 },
  data: {
    comments: {
      update: {
        where: { id: 10 },
        data: { content: '更新后的评论' },
      },
    },
  },
})

没有这条功能时,你得先查 Post → 遍历 comments 找到那条 → 再单独 update。

避免 N+1 的一般原则

  1. 用 include 代替后续单独查询——一次拉回来
  2. 用 select 只取需要的字段——减少数据量
  3. 关系筛选放在 where 里(posts.some、posts.none)而不是先拉全量再在 JS 里过滤
  4. _count 做计数,不要先拉数组再 .length