Open3
Prismaの便利な使い方
備忘録として。
環境
"prisma": "^4.15.0",
Atomic number operations
TL;DR
prisma.stock.update({
where: { id },
data: {
quantity: {
increment: quantity, // ← これ
},
},
});
概要
SQLだと簡単な インクリメント。
UPDATE stock
SET quantity = quantity + 1
WHERE id = id
的なのを実現してくれるやつ。
何がうれしい
↓こうやりがち
prisma.stock.update({
where: { id },
data: {
quantity: existsQuantity + quantity, // ← ここ
},
});
既存レコードを持ってないとUpdateできない!ってなるのを回避してくれる。
補足
下記のようなのが用意されているので、公式docへGO。
- increment
- decrement
- multiply
- divide
- set
存在したらCreate、無かったらFindでレコードを返す
TL;DR
upsert を使う。
updateプロパティ を空にするだけ。
await prisma["hoge"].upsert({
where: { id: data.id },
update: {},
create: data,
});
詳細は後述するが、無用なUPDATEが走ることもなかった。
背景
createOrFindみたいなものを実現するため、「findをして、無ければcreate」というロジックを組んでいた。
ただ、小さいとはいえ分岐が入ってくるし美しくなかった。
いろいろ試行錯誤した結果、前述の書き方に行き着いた。
調査
とはいえ、もしupdateが毎回走るのであれば、DB側の負荷増大は免れない。
分岐を自分で作ったほうがDBに優しい構造になる。
ので、テストコードで調査。
↓こんな感じにした。
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient({
log: ["query", "info", "warn", "error"],
});
const applySeed = async (seed: { code: string }, schemaName: "jan") => {
await prisma[schemaName].upsert({
where: { code: seed.code },
update: {},
create: seed,
});
};
applySeed({ code: "100" }, "jan")
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
※ 製作中のRepoからぶんどって改変した。スキーマ名とか汎用性とかはご容赦。
ログを吐かせつつ、ダミーデータでupsert。
初回実行
@acme/db:db:test: prisma:info Starting a postgresql pool with 9 connections.
@acme/db:db:test: prisma:query BEGIN
@acme/db:db:test: prisma:query SELECT "public"."Jan"."code" FROM "public"."Jan" WHERE ("public"."Jan"."code" = $1 AND 1=1) OFFSET $2
@acme/db:db:test: prisma:query INSERT INTO "public"."Jan" ("code","updatedAt","createdAt") VALUES ($1,$2,$3) RETURNING "public"."Jan"."code"
@acme/db:db:test: prisma:query SELECT "public"."Jan"."code", "public"."Jan"."updatedAt", "public"."Jan"."createdAt" FROM "public"."Jan" WHERE "public"."Jan"."code" = $1 LIMIT $2 OFFSET $3
@acme/db:db:test: prisma:query COMMIT
INSERTが走っている。
二回目実行
@acme/db:db:test: prisma:info Starting a postgresql pool with 9 connections.
@acme/db:db:test: prisma:query BEGIN
@acme/db:db:test: prisma:query SELECT "public"."Jan"."code" FROM "public"."Jan" WHERE ("public"."Jan"."code" = $1 AND 1=1) OFFSET $2
@acme/db:db:test: prisma:query SELECT "public"."Jan"."code" FROM "public"."Jan" WHERE ("public"."Jan"."code" = $1 AND 1=1)
@acme/db:db:test: prisma:query SELECT "public"."Jan"."code", "public"."Jan"."updatedAt", "public"."Jan"."createdAt" FROM "public"."Jan" WHERE "public"."Jan"."code" = $1 LIMIT $2 OFFSET $3
@acme/db:db:test: prisma:query COMMIT
SELECTのみ、UPDATEは走らなかった。
これで、upsertを createOrFindとして利用しても問題ないことが分かった。
補足
この方法は、@@id もしくは @@unique にあたるValueが分かっている場合にのみ使える。