[Prisma]カラムを他の型に変更したい時はmigration.sqlに移行クエリを書くのがよい。
Prismaでカラムを追加しようとしてprisma.schemaを見直していたら、Enum型で想定していたカラムがよく見たらEnum型ではなくString型になっていました。うっかりミスです。このままでも運用できなくはないですが気持ちが晴れないので、今まで文字列で保存していたカラムをEnumに移行することにしました。
参考
カラム名を変更する場合
ドキュメントの例。例えば以下のようなschemaがあったとして、
model Profile {
id Int @id @default(autoincrement())
biograpy String
userId Int @unique
user User @relation(fields: [userId], references: [id])
}
"biograpy"(ぐらぴー)と打ち間違えている箇所を修正したいので以下のように書き換えます。
model Profile {
id Int @id @default(autoincrement())
biography String
userId Int @unique
user User @relation(fields: [userId], references: [id])
}
このままnpx prisma migrate dev
しても良いですが、このままだと以下のようmigration.sqlが生成されます。
ALTER TABLE "Profile" DROP COLUMN "biograpy",
ADD COLUMN "biography" TEXT NOT NULL;
これではbiograpyカラムを削除し、新しいbiographyカラムを作成してしまうため、データが消えてしまいます。
なので、一度マイグレーション用のSQLを生成だけした状態にして生成されたマイグレーション用SQLを編集して理想的なクエリにしてあげる必要があります。--create-only
のオプションをつけてあげればSQLを生成した段階で作業を終えてくれます。
npx prisma migrate dev --name rename-migration --create-only
これで生成したSQLを以下のように編集します。この書き方なら正しく名前の変更のみを行うことができます。
ALTER TABLE "Profile" RENAME COLUMN "biograpy" TO "biography";
あとは変更を反映してあげればよし。ここまでがドキュメントの話です。
npx prisma migrate dev --name rename-migration
StringからEnumに変更するには?
ここからは以上の内容を踏まえて私の方で行ったことの紹介になります。今回はすでにデータは入っているが、全て同じ文字列が入っているカラムをEnumに移行しました。例えば、今後機能追加で"apple"以外にも"banana"と"cherry"も入ってくる予定だったfruits
カラムを、うっかり文字列にしていた状態です。運用できなくもなさそうですが、パターンが限られた文字列はEnumにしておきたい。
まずprisma.schemaを変更後の理想的な形に変更します。
model Sample {
id Int @id default(autoincrement)
- fruits String
+ fruits Fruits @default(apple)
}
Enum Fruits {
apple
banana
cherry
}
この状態で--create-only
でmigration.sqlを作成。
npx prisma migrate dev --name convert-fruits-to-enum --create-only
migration.sqlを以下のように編集。このSQLの中で移行作業がすべて行えるためスムーズです。
-- 1) 新しい Enum 型を作成
CREATE TYPE "Fruits" AS ENUM ('apple', 'banana', 'cherry');
-- 2) 一時的な Enum カラムを追加(NOT NULL にしておく)
ALTER TABLE "sample"
ADD COLUMN "fruits_new" "Fruits" NOT NULL DEFAULT 'apple';
-- 3) 既存データを 'apple' に更新(本件ではすべて 'apple' なのでデフォルトのままでも OK)
-- 念のため、すべての行を 'apple' にセットします。
UPDATE "sample"
SET "fruits_new" = 'apple'::"Fruits";
-- 4) 旧カラムを削除
ALTER TABLE "sample"
DROP COLUMN "friuts";
-- 5) 一時カラムを元の名前にリネーム
ALTER TABLE "sample"
RENAME COLUMN "fruits_new" TO "friuts";
-- 6) (オプション)もし NOT NULL や DEFAULT 'apple' をもう一度厳格に設定したい場合は以下を実行
ALTER TABLE "sample"
ALTER COLUMN "fruit" SET DEFAULT 'apple',
ALTER COLUMN "fruit" SET NOT NULL;
migrate dev
して完了。
npx prisma migrate dev
Expand & Contract パターン
より大きな規模のデータ移行については「Expand & Contract パターンを使ってダウンタイムなしでスキーマを変更しよう」ということ。”Expand & Contract パターン”というのが、
- 一つカラムを追加して、追加した新カラムの方に移行後のデータを入れる。(create,updateは新カラムに追加してfindなどは移行前のカラムを使用する。)
- 移行中は移行前のカラムを変わらず参照させる。
- タイミングで移行前のカラムを削除しデータ移行の完了。
という段階を踏んでデータ移行を行う手法とのことでした。
今回はそこまで大掛かりな移行作業ではなかったので前述のような移行作業を行いました。
まとめ
今回はプロジェクトがまだ小さいうちだったので変更の手間も小さくてラッキーでした。
この内容がわかっていれば今後の変更にも立ち向かえそうな気がしたので、小規模なうちに遭遇できてよかったです。コードの確認は大事。
Discussion