You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeORM中returning子句无法返回计算字段duration问题求助

解决TypeORM插入Session后返回计算列的问题

问题场景

使用PostgreSQL + TypeORM开发数据同步服务:先向session表插入数据,需要返回id、startHour、endHour,同时计算duration(endHour与startHour的时间差,转换为小时数),后续还要结合表C的hourlyRate做处理。

尝试用createQueryBuilder的returning子句返回计算列,但duration未出现在结果中:

.returning(['id','startHour','endHour','EXTRACT(EPOCH FROM(endHour - startHour) / 3600) as duration'])
console.log('sessionInsertResult', sessionInsertResult)
const session = sessionInsertResult.generatedMaps[0] as Session
console.log('session', session) // 无duration字段

单独执行SQL能正确获取duration,目前只能拆分为两次查询(插入后再查),寻求更优方案。

原因分析

TypeORM的generatedMaps会将返回结果映射到实体类定义的字段,而自定义计算列不属于实体映射字段,因此不会被自动包含到generatedMaps中。但数据库实际上已经返回了该列,只是TypeORM没把它映射进去。

最优解决方案

方案1:一次插入+返回原始结果(推荐)

使用execute()返回的raw属性获取所有数据库返回的列,无需二次查询:

const insertResult = await this.sessionRepository
  .createQueryBuilder()
  .insert()
  .into(Session)
  .values({
    date: payload.date,
    startHour: payload.startHour,
    endHour: payload.endHour,
    gymId: coach.gymId,
    coachId: coach.id,
    disciplineId: payload.disciplineId,
  })
  .returning([
    'id',
    'startHour',
    'endHour',
    'EXTRACT(EPOCH FROM ("endHour" - "startHour") / 3600) AS duration'
  ])
  .execute();

// 从raw结果中获取包含duration的完整数据
const sessionWithDuration = insertResult.raw[0];
console.log(sessionWithDuration.duration); // 正常拿到计算值

优势:仅一次数据库操作,性能最优,无需修改表结构或实体。

方案2:使用PostgreSQL生成列(适合频繁使用duration的场景)

如果duration是后续查询经常需要用到的字段,可以直接在表中定义存储生成列,让数据库自动维护该值:

  1. 修改表结构:
ALTER TABLE "session" 
ADD COLUMN "duration" NUMERIC 
GENERATED ALWAYS AS (EXTRACT(EPOCH FROM ("endHour" - "startHour")/3600)) STORED;
  1. 在Session实体类中添加对应字段:
@Column({ name: 'duration', type: 'numeric', generated: 'STORED' })
duration: number;
  1. 插入时直接返回该字段:
const insertResult = await this.sessionRepository
  .createQueryBuilder()
  .insert()
  .into(Session)
  .values(/* ... 插入数据 ... */)
  .returning(['id', 'startHour', 'endHour', 'duration'])
  .execute();

const session = insertResult.generatedMaps[0] as Session;
console.log(session.duration); // 直接拿到值

优势:后续所有查询都能直接使用duration字段,无需重复计算,代码更简洁。

方案3:用TypeORM查询替代原生SQL(折中方案)

如果不想调整插入逻辑,也可以用TypeORM的查询构造器替代原生query方法,保持代码风格统一:

const savedSession = await this.sessionRepository.save(session);

const sessionWithDuration = await this.sessionRepository
  .createQueryBuilder('s')
  .select(['s.id', 's.startHour', 's.endHour'])
  .addSelect('EXTRACT(EPOCH FROM (s.endHour - s.startHour)/3600)', 'duration')
  .where('s.id = :id', { id: savedSession.id })
  .getRawOne();

优势:比原生SQL更符合TypeORM的使用习惯,类型提示更友好。

内容的提问来源于stack exchange,提问作者yassine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 08:16:02