SQLの一対一外部キーをPrismaリレーションに変換する

UNIQUE外部キー制約がPrismaの一対一リレーションフィールドにどのように変換されるかを学びます。@relationを使用したオプショナルおよび必須の一対一リレーションを解説します。

Relations

詳細な説明

一対一リレーション

SQLにおける一対一リレーションシップは、UNIQUE制約付きの外部キーであり、各親が最大1つの関連する子を持つことを保証します。Prismaは両側の単数リレーションフィールドでこれをモデル化します。

SQLの例

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) NOT NULL
);

CREATE TABLE profiles (
  id SERIAL PRIMARY KEY,
  user_id INTEGER NOT NULL UNIQUE,
  bio TEXT,
  avatar_url VARCHAR(2048),
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

生成されるPrismaスキーマ

model User {
  id      Int      @id @default(autoincrement())
  name    String
  profile Profile?

  @@map("users")
}

model Profile {
  id        Int     @id @default(autoincrement())
  userId    Int     @unique @map("user_id")
  bio       String? @db.Text
  avatarUrl String? @db.VarChar(2048) @map("avatar_url")
  user      User    @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@map("profiles")
}

仕組み

  1. 外部キーカラムuser_idUNIQUE制約がある — これが1:1リレーションシップを示します。
  2. Prismaでは、子model(Profile)がfieldsreferencesを持つ@relationを保持します。
  3. 親model(User)はオプショナルな逆参照Profile?を持ちます(プロファイルがまだ存在しない可能性があるためオプショナル)。
  4. PrismaのuserIdに対する@unique属性が一対一制約を強制します。

必須 vs オプショナル

// オプショナル:ユーザーはプロファイルを持っていない場合がある
model User {
  profile Profile?
}

// 必須:すべてのユーザーがプロファイルを持つ必要がある
model User {
  profile Profile
}

親側でリレーションがオプショナルかどうかはビジネスロジックに依存します。SQLスキーマだけではこれを表現できません — 子側のNOT NULL FKは「すべてのプロファイルにユーザーが必要」を意味しますが、「すべてのユーザーにプロファイルが必要」を意味するわけではありません。

埋め込みとの比較

別の1:1テーブルの代替として、すべてのフィールドを親テーブルに配置する方法があります:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  bio TEXT,
  avatar_url VARCHAR(2048)
);

以下の場合に別テーブルを使用してください:

  • 関連データが大きく、アクセス頻度が低い
  • 独立した権限制御が必要
  • 関連データが独自のライフサイクルを持つ(例:独自のcreated_at/updated_at)

コンバーターはUNIQUE外部キーを検出すると1:1リレーションを生成し、双方向のリレーションフィールドを自動的に追加します。

自己参照型の一対一

より一般的でないパターンとして、自己参照1:1があります:

CREATE TABLE employees (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  mentor_id INTEGER UNIQUE,
  FOREIGN KEY (mentor_id) REFERENCES employees(id)
);
model Employee {
  id       Int       @id @default(autoincrement())
  name     String
  mentorId Int?      @unique @map("mentor_id")
  mentor   Employee? @relation("EmployeeMentor", fields: [mentorId], references: [id])
  mentee   Employee? @relation("EmployeeMentor")

  @@map("employees")
}

ユースケース

usersテーブルとUNIQUE外部キーでリンクされた別のprofilesテーブルがある場合に、コンバーターが正しい@unique制約とカスケード動作で両方のmodelにPrismaの一対一リレーションフィールドを生成します。

試してみる — SQL to Prisma Schema

フルツールを開く