构建房地产SQL Schema:显式多对多关系及替代方案咨询
Prisma + PlanetScale 下 Listing 与 ListingType 关联方案
现有显式多对多方案调整
因为PlanetScale不支持数据库外键,你之前的隐式多对多会报错,改成显式多对多后,还得调整Prisma配置,让它绕过数据库外键约束,自己处理关系。
调整后的完整Prisma模型代码:
generator client { provider = "prisma-client-js" relationMode = "prisma" // 关键:让Prisma自己管理关系,不依赖数据库外键 } datasource db { provider = "mysql" url = env("DATABASE_URL") referentialIntegrity = "prisma" // 配合上面的relationMode使用 } enum ListingTypeEnum { SALE RENT } enum ListingStatus { ARCHIVED ACTIVE } enum ListingVisibility { PUBLIC PRIVATE } model User { id Int @id @default(autoincrement()) listings Listing[] } model Listing { id Int @id @default(autoincrement()) name String slug String @unique bio String price Int userId Int user User @relation(fields: [userId], references: [id], onDelete: Cascade) status ListingStatus @default(ARCHIVED) visibility ListingVisibility @default(PUBLIC) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt listingTypes ListingOnListingType[] } model ListingType { id Int @id @default(autoincrement()) name ListingTypeEnum @unique listings ListingOnListingType[] } model ListingOnListingType { listingId Int @db.Int listingTypeId Int @db.Int assignedAt DateTime @default(now()) assignedBy String listing Listing @relation(fields: [listingId], references: [id]) listingType ListingType @relation(fields: [listingTypeId], references: [id]) @@id([listingId, listingTypeId]) }
- 重点就是开启
relationMode = "prisma",让Prisma在应用层维护关系,不用数据库的外键约束,刚好适配PlanetScale的要求。 - 中间表保留复合主键,保证同一个Listing和ListingType只会关联一次。
替代方案:位掩码存储
如果觉得多对多太繁琐,且ListingType只有SALE、RENT这类固定选项,可以用位掩码存储,不用额外建表。
修改Listing模型,新增typeFlags字段:
model Listing { // 其他原有字段不变 typeFlags Int @default(0) // 1=SALE,2=RENT,3=同时包含两种类型 }
业务代码里这么处理:
- 标记为SALE:
typeFlags |= 1 - 标记为RENT:
typeFlags |= 2 - 查询同时具备两种类型的Listing:
const listings = await prisma.listing.findMany({ where: { typeFlags: 3 } });
- 查询包含SALE或RENT的Listing:
const listings = await prisma.listing.findMany({ where: { typeFlags: { in: [1,2,3] } } });
优点是无需额外表,查询逻辑简单;缺点是后续新增类型需调整位值,可读性不如多对多方案。
显式多对多的查询示例
要查询同时具备SALE和RENT的Listing,用Prisma可以这么写:
const listingsWithBothTypes = await prisma.listing.findMany({ where: { AND: [ { listingTypes: { some: { listingType: { name: ListingTypeEnum.SALE } } } }, { listingTypes: { some: { listingType: { name: ListingTypeEnum.RENT } } } } ] }, include: { listingTypes: { include: { listingType: true } } } });
内容的提问来源于stack exchange,提问作者Josue Mata
相关产品推荐
相关产品推荐

