自定义迁移
在某些情况下,您需要在应用迁移文件之前对其进行编辑。例如,为了在不丢失数据的情况下更改 1-1 关系的方向(将外键从一侧移动到另一侧),您需要在迁移过程中移动数据 - 此 SQL 不包含在默认迁移中,必须手动编写。
本指南解释了如何编辑迁移文件,并提供了一些您可能想要这样做的用例示例。
如何编辑迁移文件
要在应用迁移文件之前对其进行编辑,一般步骤如下
-
进行需要自定义 SQL 的 schema 更改(例如,为了保留现有数据)
-
使用以下命令创建草稿迁移
npx prisma migrate dev --create-only
-
修改生成的 SQL 文件。
-
通过运行以下命令应用修改后的 SQL
npx prisma migrate dev
示例:重命名字段
默认情况下,在 schema 中重命名字段会导致迁移执行以下操作
- `CREATE` 一个新列(例如,`fullname`)
- `DROP` 现有列(例如,`name`)以及该列中的数据
要真正**重命名**一个字段,并在生产环境中运行迁移时避免数据丢失,您需要在将其应用到数据库之前修改生成的迁移 SQL。考虑以下 schema 片段 - `biograpy` 字段拼写错误。
model Profile {
id Int @id @default(autoincrement())
biograpy String
userId Int @unique
user User @relation(fields: [userId], references: [id])
}
要将 `biograpy` 字段重命名为 `biography`
-
在 schema 中重命名字段
model Profile {
id Int @id @default(autoincrement())
biograpy String
biography String
userId Int @unique
user User @relation(fields: [userId], references: [id])
} -
运行以下命令以创建一个**草稿迁移**,您可以在将其应用到数据库之前对其进行编辑
npx prisma migrate dev --name rename-migration --create-only
-
如图所示编辑草稿迁移,将 `DROP` / `DELETE` 更改为单个 `RENAME COLUMN`
- 之前
- 之后
./prisma/migrations/20210308092620_rename_migration/migration.sqlALTER TABLE "Profile" DROP COLUMN "biograpy",
ADD COLUMN "biography" TEXT NOT NULL;./prisma/migrations/20210308092620_rename_migration/migration.sqlALTER TABLE "Profile"
RENAME COLUMN "biograpy" TO "biography"对于 SQL Server,您应该使用存储过程 `sp_rename` 而不是 `ALTER TABLE RENAME COLUMN`。
./prisma/migrations/20210308092620_rename_migration/migration.sqlEXEC sp_rename 'dbo.Profile.biograpy', 'biography', 'COLUMN';
-
保存并应用迁移
npx prisma migrate dev
您可以使用相同的技术来重命名 `model` - 编辑生成的 SQL 以*重命名*表,而不是删除并重新创建它。
示例:使用扩展和收缩模式在不停机的情况下演化 schema
对现有字段进行 schema 更改,例如重命名字段,可能会导致停机。这种情况发生在应用修改现有字段的迁移与部署使用修改后字段的新版本应用程序代码之间的时间段内。
您可以通过将更改字段所需的步骤分解为一系列旨在逐步引入更改的离散步骤来防止停机。这种模式被称为*扩展和收缩模式*。
此模式涉及两个组件:访问数据库的应用程序代码和您打算更改的数据库 schema。
使用*扩展和收缩*模式,使用 Prisma 将字段 `bio` 重命名为 `biography` 将如下所示
-
将新的 `biography` 字段添加到您的 Prisma schema 并创建一个迁移
model Profile {
id Int @id @default(autoincrement())
bio String
biography String
userId Int @unique
user User @relation(fields: [userId], references: [id])
} -
*扩展*:更新应用程序代码并同时写入 `bio` 和 `biography` 字段,但继续从 `bio` 字段读取,并部署代码
-
创建一个空迁移并将现有数据从 `bio` 复制到 `biography` 字段
npx prisma migrate dev --name copy_biography --create-only
prisma/migrations/20210420000000_copy_biography/migration.sqlUPDATE "Profile" SET biography = bio;
-
验证数据库中 `biography` 字段的完整性
-
更新应用程序代码以从新的 `biography` 字段**读取**
-
更新应用程序代码以**停止写入** `bio` 字段
-
*收缩*:从 Prisma schema 中删除 `bio`,并创建一个迁移来删除 `bio` 字段
model Profile {
id Int @id @default(autoincrement())
bio String
biography String
userId Int @unique
user User @relation(fields: [userId], references: [id])
}npx prisma migrate dev --name remove_bio
通过使用这种方法,您可以避免更改应用程序代码中使用的现有字段可能导致的潜在停机时间,并减少应用迁移和部署更新后的应用程序代码之间所需的协调量。
请注意,这种模式适用于任何涉及更改具有数据且正在被应用程序代码使用的列的情况。示例包括将两个字段合并为一个,或将 `1:n` 关系转换为 `m:n` 关系。
要了解更多信息,请查看数据指南文章,了解 扩展和收缩模式
示例:更改 1-1 关系的方向
要更改 1-1 关系的方向
-
在 schema 中进行更改
model User {
id Int @id @default(autoincrement())
name String
posts Post[]
profile Profile? @relation(fields: [profileId], references: [id])
profileId Int @unique
}
model Profile {
id Int @id @default(autoincrement())
biography String
user User
} -
运行以下命令以创建一个**草稿迁移**,您可以在将其应用到数据库之前对其进行编辑
npx prisma migrate dev --name rename-migration --create-only
显示CLI结果⚠️ There will be data loss when applying the migration:
• The migration will add a unique constraint covering the columns `[profileId]` on the table `User`. If there are existing duplicate values, the migration will fail. -
如图所示编辑草稿迁移
- 之前
- 之后
-- DropForeignKey
ALTER TABLE "Profile" DROP CONSTRAINT "Profile_userId_fkey";
-- DropIndex
DROP INDEX "Profile_userId_unique";
-- AlterTable
ALTER TABLE "Profile" DROP COLUMN "userId";
-- AlterTable
ALTER TABLE "User" ADD COLUMN "profileId" INTEGER NOT NULL;
-- CreateIndex
CREATE UNIQUE INDEX "User_profileId_unique" ON "User"("profileId");
-- AddForeignKey
ALTER TABLE "User" ADD FOREIGN KEY ("profileId") REFERENCES "Profile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- DropForeignKey
ALTER TABLE "Profile" DROP CONSTRAINT "Profile_userId_fkey";
-- DropIndex
DROP INDEX "Profile_userId_unique";
-- AlterTable
ALTER TABLE "User" ADD COLUMN "profileId" INTEGER;
UPDATE "User"
SET "profileId" = "Profile".id
FROM "Profile"
WHERE "User".id = "Profile"."userId";
ALTER TABLE "User" ALTER COLUMN "profileId" SET NOT NULL;
-- AlterTable
ALTER TABLE "Profile" DROP COLUMN "userId";
-- CreateIndex
CREATE UNIQUE INDEX "User_profileId_unique" ON "User"("profileId");
-- AddForeignKey
ALTER TABLE "User" ADD FOREIGN KEY ("profileId") REFERENCES "Profile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-
保存并应用迁移
npx prisma migrate dev