TOOLS BOX/ガイド/select vs include
Concept

select vs include

Prisma の select と include の違いと使い分けを整理するガイド。取得フィールドの絞り込みとリレーション展開の役割差を比較する。

prismaselectincluderelationover-fetchingN+1

どういう場面で使うか

  • ·select: API レスポンスに必要なフィールドだけを返したいとき、機密フィールドを除外したいとき
  • ·include: リレーション先モデルを 1 クエリで一緒に取得したいとき(N+1 回避)
  • ·select + ネスト select: リレーション先のフィールドも絞りたいとき

注意点 / Pitfalls

  • ·include と select は同一クエリの同一レベルで併用できない(どちらか一方を選ぶ)
  • ·include: { relation: true } はリレーション先の全フィールドを返すため over-fetching になりやすい
  • ·select を省略すると全フィールドが返るため、パスワードハッシュなど機密フィールドの漏洩に注意

補足

リレーション先フィールドを絞りたい場合は include の代わりに select + ネスト select を選ぶ。どちらの戻り値型も Prisma が自動推論する。

何が違うか

selectinclude
役割取得フィールドを明示的に絞るリレーション先モデルを展開する
通常フィールドの返し方指定したものだけすべて返る
リレーションネスト select で絞れる全フィールドが返る(デフォルト)
同一レベルでの併用不可不可
型推論絞り込まれた型が自動生成される展開したリレーションを含む型が生成される
// select: id と title だけ取得
const posts = await prisma.post.findMany({
  select: { id: true, title: true },
});

// include: 全フィールド + author を展開
const posts = await prisma.post.findMany({
  include: { author: true },
});

select が向く場面

  • API レスポンスに含めるフィールドを最小限に抑えたいとき
  • パスワードハッシュや機密フィールドを誤って返さないようにしたいとき
  • リレーション先からも特定フィールドだけほしいとき(ネスト select
// author の name だけほしい場合は select + ネスト
prisma.post.findMany({
  select: {
    id: true,
    title: true,
    author: { select: { name: true } }, // name だけに絞れる
  },
});

include が向く場面

  • リレーション先のフィールドをすべて使いたいとき
  • ループ外で 1 クエリにまとめて N+1 を回避したいとき
  • リレーション件数(_count)だけ取りたいとき
// posts と著者を 1 クエリで取得(N+1 回避)
prisma.user.findMany({
  include: {
    posts: {
      where: { published: true },
      orderBy: { createdAt: "desc" },
      take: 5,
    },
  },
});

over-fetching と N+1 への対処

over-fetching(include を使う場合)

include: { author: true } は著者モデルの全フィールドを返します。emailpasswordHash など不要なフィールドが多い場合は select + ネスト select に切り替えます。

// over-fetching になりやすい
include: { author: true }

// フィールドを絞る
select: {
  id: true,
  author: { select: { name: true, avatarUrl: true } },
}

N+1(include を使わない場合)

ループ内で都度 findUnique を呼ぶと N+1 になります。include または Promise.all でまとめて取得します。

// ❌ N+1: posts.length 回クエリが走る
for (const post of posts) {
  const author = await prisma.user.findUnique({ where: { id: post.authorId } });
}

// ✅ 1 クエリで解決
const postsWithAuthor = await prisma.post.findMany({
  include: { author: true },
});

ネストが深くなるとき

select / include はネストできますが、3 段以上になると可読性が下がります。深いネストが必要な場合は、クエリを分割して Promise.all で並列実行する方がコードが追いやすくなります。

// ネストが深い例(読みにくくなりがち)
prisma.post.findMany({
  include: {
    author: {
      include: {
        profile: {
          include: { address: true },
        },
      },
    },
  },
});

判断フロー

リレーション先が必要か?
├── No  → select のみで OK
└── Yes → リレーション先のフィールドを絞りたいか?
          ├── No  → include: { relation: true }
          └── Yes → select + ネスト select

関連サンプル

同じテーマや技術スタックを使った実装例