因为大量熟练使用 database tools,如:Prisma 和 Ecto,我已经不能熟练写出相对复杂 SQL 了。😭
目前,我在设计和开发一个能源回收和调度系统,因为我是一个以“懒”作为美德的程序员,我想直接使用 Supabase 提供的 Auth 服务。
但是我发现在 Prisma schema 中实现的 many-to-many 关系,无法优雅地使用 supabase-js 查询。
于是我研究了一下原因,不看不知道,一看吓一跳。Prisma Migration 生成的 many-to-many SQL 好复杂,完全违反直觉,反正我 review 困难。
下面是一个 role:permission many-to-many 的例子:
// prisma.schema
model Permission {
id Int @id @default(autoincrement())
roles Role[]
}
model Role {
id Int @id @default(autoincrement())
permissions Permission[]
}
The SQL was generated by Prisma Migration:
-- CreateTable
CREATE TABLE "_PermissionToRole" (
"A" INTEGER NOT NULL,
"B" INTEGER NOT NULL
);
-- CreateIndex
CREATE UNIQUE INDEX "_PermissionToRole_AB_unique" ON "_PermissionToRole"("A", "B");
-- CreateIndex
CREATE INDEX "_PermissionToRole_B_index" ON "_PermissionToRole"("B");
-- AddForeignKey
ALTER TABLE "_PermissionToRole" ADD CONSTRAINT "_PermissionToRole_A_fkey" FOREIGN KEY ("A") REFERENCES "Permission"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_PermissionToRole" ADD CONSTRAINT "_PermissionToRole_B_fkey" FOREIGN KEY ("B") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE;
上面 SQL 看的我一愣一愣的。而且上面 SQL 生成的关系在 supabase-js 查询语句很丑。
const { data, error } = await supabase.from("Role").select(`*, _PermissionToRole(
A: Permission (*)
)`);
于是我凭着记忆手写了下面这段 SQL。
create table role (
"id" serial primary key,
);
create table permission (
"id" serial primary key,
);
create table members (
"role_id" int references role,
"permission_id" int references permission,
primary key (role_id, permission_id)
);
经过测试,我手写的 SQL 可以使用 supabase-js 进行关联查询,代码如下:
- const { data, error } = await supabase.from("Role").select(`*, _PermissionToRole(
- A: Permission (*)
- )`);
+ const {data, error} = await supabase.from("role").select("*, permission(*)")
答:不会,如果是团队合作的 JS 项目,我依然会使用 Prisma Migration,因为复杂的关系和限制手写 SQL 成本太高了。工作量、易出错、缺少检查工具。
所以,由于种种原因,我的结论:
Don't review Prisma Migration generated SQL, focus on prisam.schema.
TJ 2024-03-07
我会使用 Ecto 管理 migrations。
Ecto 生成的 many-to-many SQL 简洁又准确
create table(:role_permission, primary_key: false) do
add :role_id, references(:role), primary_key: true
add :permission_id, references(:permission), primary_key: true
end
-- Table Definition
CREATE TABLE "public"."role_permission" (
"role_id" int8 NOT NULL,
"permission_id" int8 NOT NULL,
CONSTRAINT "role_permission_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "public"."role"("id"),
CONSTRAINT "role_permission_permission_id_fkey" FOREIGN KEY ("permission_id") REFERENCES "public"."permission"("id"),
PRIMARY KEY ("role_id","permission_id")
);
对比 Prsima 生成的 SQL,高下立判。