如何不使用$queryRaw在Prisma中执行PostgreSQL的to_char函数
可行方案说明
截至Prisma当前正式稳定版本,不存在完全不使用原生查询、仅靠内置链式语法直接调用to_char等PostgreSQL原生函数的实现方式。你预期的在findMany参数内直接传入数据库函数的写法,目前官方没有支持计划——这类和特定数据库绑定的函数语法差异极大,作为跨数据库ORM,Prisma不会内置覆盖所有数据库方言的私有函数。
如果不想写整段原生SQL,可以用以下两种可落地的方案实现需求:
方案1:新增数据库存储生成列(推荐,完全符合无原生查询要求)
通过Prisma支持的dbgenerated属性,在表中新增一个由数据库自动维护的冗余字段,预存日期对应的月日数值,后续所有筛选排序直接针对该字段操作,全程使用Prisma普通查询语法。
- 首先修改Prisma Schema,新增生成列:
model User { id Int @id @default(autoincrement()) birthdate DateTime // 数据库自动计算存储MMDD格式数值,例:10月5日存储为1005,无需业务代码手动传值 birthdateMmdd Int @default( dbgenerated("EXTRACT(MONTH FROM birthdate)::int * 100 + EXTRACT(DAY FROM birthdate)::int") ) // 其余业务字段... }
修改后执行prisma migrate dev生成并执行数据库迁移即可,历史数据的birthdateMmdd字段会由数据库自动补全。
- 业务代码中直接使用普通查询实现需求:
// 计算当前日期对应的MMDD数值 const now = new Date() const currentMmdd = (now.getMonth() + 1) * 100 + now.getDate() // 完全走Prisma标准链式调用,无任何原生SQL const userList = await this.prismaService.user.findMany({ where: { birthdateMmdd: { gte: currentMmdd } }, orderBy: { birthdateMmdd: 'asc' } })
该方案优缺点:
- 优势:完全满足无原生查询的要求,查询性能远高于运行时逐行调用
to_char,可以给birthdateMmdd加索引进一步提升大表查询速度 - 劣势:需要修改表结构,已上线的大表需要执行迁移操作
方案2:保留现有原生查询写法(无需改表)
如果你完全不能接受修改表结构,你目前使用的$queryRaw参数化查询写法已经是不改表前提下的最优实现,不存在其他绕开原生查询的取巧方式。注意写原生查询时始终使用Prisma.sql标签做参数转义,避免SQL注入风险即可。
注:不要尝试通过Prisma中间件、客户端扩展等能力hack查询语法实现类似效果,这类实现本质还是在底层拼接原生SQL,维护成本和稳定性远不如上面两种方案。
内容的提问来源于stack exchange,提问作者Birtalan Robert
相关产品推荐
相关产品推荐

