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

如何使用MikroORM查询构建器实现指定自连接SQL查询

问题描述

需要用MikroORM查询构建器实现以下SQL查询:

SELECT * FROM (
    SELECT * FROM `gps_log`                                           -- Same table
    WHERE log_time >= DATE_SUB(NOW(), INTERVAL 232324324 MINUTE)
) as g
JOIN (SELECT MAX(gps_log_id) as gps_id, consignment_consignment_id
      FROM `gps_log`                                                   -- Same table
      GROUP BY consignment_consignment_id
     ) AS max ON max.gps_id = g.gps_log_id 

目前尝试通过获取Knex实例执行原生SQL,但觉得不够简洁且未成功,代码如下:

const qb = this.em.createQueryBuilder(GpsLog);
const knex = qb.getKnexQuery(); // instance of Knex' QueryBuilder

knex.select("*")
    .fromRaw(
        "(SELECT * FROM `gps_log` WHERE log_time >= DATE_SUB(NOW(), INTERVAL 232324324 MINUTE)) as g"
    )
    .joinRaw(
        "(SELECT MAX(gps_log_id) as gps_id, consignment_consignment_id FROM `gps_log` GROUP BY consignment_consignment_id) AS max ON max.gps_id = g.gps_log_id"
    )

const res = await this.em.getConnection().execute(knex);
const result = res.map(a => this.em.map(GpsLog, a));

return result;

实体类定义:

@Entity()
export class GpsLog {

    /* SQL properties */
    @PrimaryKey({ hidden: true })
    gpsLogId!: number;

    @Property({ type: types.double })
    long!: string;
    
    @Property({ type: types.double })
    lat!: string;

    @Property()
    logTime: Date;


    /* JSON Formatter */
    @Property({ persist: false })
    get self() {
        return `/consgts/${this.consignment}/logs/${this.gpsLogId}`;
    }

    @Property({ persist: false })
    get consgt() {
        return `/consgts/${this.consignment}`;
    }


    /* SQL Relationships */
    @ManyToOne(() => Consignment, { mapToPk: true, hidden: true })
    consignment!: Consignment;
}
解决方案

可以通过MikroORM查询构建器的子查询和显式连接来实现,无需直接写原生SQL片段,具体代码如下:

const em = this.em;

// 构建第一个子查询:筛选最近指定分钟内的GPS日志
const subQueryG = em.createQueryBuilder(GpsLog, 'g')
  .select('*')
  .where('g.logTime >= DATE_SUB(NOW(), INTERVAL 232324324 MINUTE)');

// 构建第二个子查询:按运单分组取每个运单的最新GPS日志ID
const subQueryMax = em.createQueryBuilder(GpsLog, 'gl')
  .select(['MAX(gl.gpsLogId) as gps_id', 'gl.consignment as consignment_consignment_id'])
  .groupBy('gl.consignment');

// 主查询:连接两个子查询
const rawResult = await em.createQueryBuilder()
  .select('*')
  .from(subQueryG, 'g')
  .join(subQueryMax, 'max', 'max.gps_id = g.gpsLogId')
  .getResultList();

// 将查询结果映射为GpsLog实体
const gpsLogs = rawResult.map(row => em.map(GpsLog, row));

return gpsLogs;

关键说明:

  • 用createQueryBuilder分别构建两个子查询,直接使用实体属性名(如logTime、gpsLogId),MikroORM会自动映射到对应的数据库字段。
  • 主查询通过from(subQueryG, 'g')将第一个子查询作为数据源,再用join(subQueryMax, 'max', 'max.gps_id = g.gpsLogId')完成关联。
  • 最后用getResultList()获取原始结果,通过em.map映射为实体实例,保留实体的属性和方法。

这种写法既符合MikroORM的使用规范,又精准复现了目标SQL的逻辑,比直接操作Knex原生API更简洁易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 10:25:33