如何在TypeORM中实现两张数据表的UNION操作?两种尝试方案均未达预期效果
在TypeORM中实现两张表的UNION操作解决方案
我来帮你搞定TypeORM里的UNION操作问题,先拆解下你两个方案的问题所在,再给出可行的实现方式:
方案一的问题与修正
你的方案一最后一步出错是因为createdEntity是内存中的实体实例集合,并不是数据库中的表或者可查询的子查询,所以QueryBuilder的from方法无法直接使用它。其实完全不需要把UNION结果插入新实体,直接构造包含UNION的查询即可:
修正后的方案一
先构造两个子查询的SQL语句:
const tableUnQuery = this.tableUnion.createQueryBuilder('tu') .select('id') .addSelect('description', 'name') .getQuery(); const tableTgQuery = this.tags.createQueryBuilder('tg') .select(['id', 'name']) .getQuery();
直接执行UNION查询获取结果:
const unionResult = await entityManager.query(`${tableUnQuery} UNION ${tableTgQuery}`);
如果需要继续用QueryBuilder对UNION结果进行操作(比如排序、分页),可以把UNION作为子查询传入from:
const result = await connection.createQueryBuilder() .select('*') .from(`(${tableUnQuery} UNION ${tableTgQuery})`, 'combined') .orderBy('combined.id', 'ASC') .getRawMany();
方案二的问题与正确写法
你的方案二用了addFrom,这个方法是用来添加额外的关联表(实现JOIN类的多表查询),而不是合并行的UNION操作。TypeORM的QueryBuilder提供了原生的union/unionAll方法来实现这个需求,更简洁也更符合ORM的使用方式:
正确的UNION实现(方案二修正版)
// 构建UNION查询 const unionQuery = connection.createQueryBuilder() // 第一个子查询:从TableUnion取数据,列别名要和第二个查询一致 .select('tu.id', 'id') .addSelect('tu.description', 'name') .from(TableUnion, 'tu') // 调用union方法合并第二个查询 .union( // 第二个子查询:从TagsEntity取数据 qb => qb .select('tg.id', 'id') .addSelect('tg.name', 'name') .from(TagsEntity, 'tg'), false // false表示使用UNION(自动去重),true表示UNION ALL(保留重复行) ); // 获取最终结果 const result = await unionQuery.getRawMany();
额外优化:对UNION结果分页/排序
如果需要对合并后的结果进行分页或排序,直接在查询构建器上追加方法即可:
const paginatedResult = await unionQuery .orderBy('id', 'DESC') .limit(20) .offset(10) .getRawMany();
关键注意事项
- 列一致性:UNION要求两个子查询的列数、数据类型必须完全匹配,所以一定要给列设置相同的别名(比如两个查询都输出
id和name)。 - 性能选择:如果不需要去重,优先用
unionAll(第二个参数传true),性能比UNION更好。
内容的提问来源于stack exchange,提问作者Vladimir Golub
相关产品推荐
相关产品推荐

