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是后续查询经常需要用到的字段,可以直接在表中定义存储生成列,让数据库自动维护该值:
- 修改表结构:
ALTER TABLE "session" ADD COLUMN "duration" NUMERIC GENERATED ALWAYS AS (EXTRACT(EPOCH FROM ("endHour" - "startHour")/3600)) STORED;
- 在
Session实体类中添加对应字段:
@Column({ name: 'duration', type: 'numeric', generated: 'STORED' }) duration: number;
- 插入时直接返回该字段:
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
相关产品推荐
相关产品推荐

