TypeORM中.limit()与.take()、.offset()与.skip()的区别及用法
TypeORM 分页查询方法差异与使用指南
核心差异底层原理
limit()和offset()是直接映射SQL原生的LIMIT、OFFSET参数,作用在关联join拼接完成后的整行结果集上。当查询存在关联时,1条主实体数据可能对应N条关联子数据,最终拼接后的结果行数会远多于主实体数量,此时用limit()设置返回10条,实际拿到的主实体数量会远小于预期值。
而take()和skip()是TypeORM封装的分页逻辑,会先对主表做分页过滤,再执行关联查询,最终返回的主实体数量严格等于take()设置的数值,完全符合分页场景对主实体数量的要求。
各方法适用场景
take()/skip():通用分页首选方案,所有需要按主实体数量分页的场景都可以使用,尤其适合带关联join的分页场景limit()/offset():仅适合无关联join的单表查询,或者明确需要对join拼接后的原始行结果做限制的特殊统计场景
代码示例
1. 单表查询(两种方案效果一致)
// 方案1:使用limit/offset const userList1 = await dataSource .getRepository(User) .createQueryBuilder("user") .where("user.status = :status", { status: 1 }) .offset(10) // 跳过前10条结果 .limit(5) // 取5条结果 .getMany() // 方案2:使用take/skip,和上面返回结果完全相同 const userList2 = await dataSource .getRepository(User) .createQueryBuilder("user") .where("user.status = :status", { status: 1 }) .skip(10) // 跳过前10个用户 .take(5) // 取5个用户 .getMany()
2. 带关联的分页场景(必须使用take/skip)
// 正确用法:返回10个符合条件的用户,每个用户携带关联的订单列表 const userWithOrders = await dataSource .getRepository(User) .createQueryBuilder("user") .leftJoinAndSelect("user.orders", "order") .where("user.age > :age", { age: 18 }) .skip(20) // 跳过前20个符合条件的用户 .take(10) // 严格返回10个用户实体 .getMany() // 错误用法:limit作用在join后的结果行,1个用户对应多个订单会占用多行,最终返回的用户数量远小于10 const wrongResult = await dataSource .getRepository(User) .createQueryBuilder("user") .leftJoinAndSelect("user.orders", "order") .where("user.age > :age", { age: 18 }) .offset(20) .limit(10) .getMany()
3. limit()特殊适用场景:统计关联行结果
// 查询最近10条订单交易记录(按行统计,不需要按用户聚合) const recentOrderRecords = await dataSource .getRepository(User) .createQueryBuilder("user") .leftJoinAndSelect("user.orders", "order") .orderBy("order.payTime", "DESC") .limit(10) // 限制返回10条订单行记录,符合需求 .getRawMany()
内容的提问来源于stack exchange,提问作者Denys Rybkin
相关产品推荐
相关产品推荐

