TypeORM queryBuilder如何按关联count映射字段实现orderBy排序
问题说明
查询关联registered_students(多对多关联Student实体)的Course实体时,已通过loadRelationCountAndMap方法映射非表字段students_count统计课程关联学生数,但无法直接对该字段排序,现有代码如下:
return await this.courseRepository .createQueryBuilder('course') .where('course.teacherId = :teacherId', { teacherId: teacher.id }) .leftJoin('course.registered_students', 'student') .addSelect('student.firstName') .addSelect('student.lastName') .loadRelationCountAndMap( 'course.students_count', 'course.registered_students', 'students_count', ) .getMany();
直接添加.orderBy('course.students_count', 'DESC')会报错,原因是students_count不是Course表的实际数据库字段,是TypeORM在SQL查询执行完成后在内存中给实体追加的属性,数据库层面识别不到该字段。
可行实现方案
方案1:SQL层聚合统计+排序,保留原有映射逻辑
直接在查询构建阶段手动写COUNT聚合逻辑,在SQL执行阶段就拿到学生计数,直接对聚合别名排序,不需要改动原有实体定义:
return await this.courseRepository .createQueryBuilder('course') .leftJoin('course.registered_students', 'student') .addSelect('student.firstName') .addSelect('student.lastName') // SQL层统计关联学生数,指定别名 .addSelect('COUNT(DISTINCT student.id)', 'students_count_sort') .where('course.teacherId = :teacherId', { teacherId: teacher.id }) // 按课程主键分组,兼容ONLY_FULL_GROUP_BY规则 .groupBy('course.id') // 直接对SQL层的统计值排序 .orderBy('students_count_sort', 'DESC') // 保留原有映射逻辑,将统计值挂到course实体的students_count属性 .loadRelationCountAndMap( 'course.students_count', 'course.registered_students', 'students_count', ) .getMany();
注意:COUNT里加
DISTINCT是为了避免多对多关联产生重复数据导致计数不准,分组时如果数据库开启了ONLY_FULL_GROUP_BY校验,需要把查询中所有非聚合字段都加到groupBy里。
方案2:原生结果手动映射,减少冗余查询
如果不想保留loadRelationCountAndMap额外生成的计数查询,可以直接拿原生查询结果手动映射实体,性能更好:
const queryResult = await this.courseRepository .createQueryBuilder('course') .leftJoin('course.registered_students', 'student') .addSelect('student.firstName', 'student_firstName') .addSelect('student.lastName', 'student_lastName') .addSelect('COUNT(DISTINCT student.id)', 'students_count') .where('course.teacherId = :teacherId', { teacherId: teacher.id }) .groupBy('course.id') .orderBy('students_count', 'DESC') .getRawAndEntities(); // 将统计值挂载到实体上 return queryResult.entities.map((course, idx) => { course.students_count = Number(queryResult.raw[idx].students_count); return course; });
注意事项
- 所有排序逻辑必须在SQL执行阶段完成,不要尝试在
getMany()拿到结果后用数组sort方法排序,分页场景下会导致数据错乱。 - 多条件排序直接追加
addOrderBy即可,例:.orderBy('students_count_sort', 'DESC').addOrderBy('course.createdAt', 'ASC')。
内容的提问来源于stack exchange,提问作者Pierre_T
相关产品推荐
相关产品推荐

