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

MikroORM QueryBuilder查询报错:字段需在GROUP BY或聚合函数中

解决QueryBuilder聚合查询的GROUP BY报错问题

这个需求完全能用QueryBuilder实现,你遇到的报错是因为数据库(大概率是PostgreSQL)的SQL严格模式限制:当查询中包含聚合函数(比如你用的sum())时,所有非聚合的查询字段必须出现在GROUP BY子句中,否则数据库无法确定这些字段如何与聚合结果对应。

下面提供两种可行的修改方案:

方案一:添加GROUP BY子句

把所有非聚合字段加入GROUP BY,确保每个字段都被分组(建议同时包含用户表主键,保证分组的唯一性)。修改后的代码如下:

return this.qb('user')
  .where({ id })
  .leftJoin('user.transactions', 'transactions')
  .leftJoin('user.wallet', 'wallet')
  .leftJoin('user.userLevel', 'userLevel')
  .select([
    'avatarUrl',
    'otpEnabled',
    'userName',
    'profilePrivacy',
    'userType',
    'userLevel.level',
    'loginType',
    'createdAt',
    'wallet.balance',
    this.qb()
      .where({
        transactions: {
          transactionType: {
            name: TransactionTypeName.TipSent,
          },
        },
      })
      .select('sum(transactions.amount)')
      .as('totalTipSent'),
    this.qb()
      .where({
        transactions: {
          transactionType: {
            name: TransactionTypeName.TipReceived,
          },
        },
      })
      .select('sum(transactions.amount)')
      .as('totalTipReceived'),
  ])
  // 加入所有非聚合字段到GROUP BY
  .groupBy([
    'user.id',
    'avatarUrl',
    'otpEnabled',
    'userName',
    'profilePrivacy',
    'userType',
    'userLevel.level',
    'loginType',
    'createdAt',
    'wallet.balance'
  ])
  .execute('get');

方案二:用子查询独立计算聚合值

如果不想维护大量GROUP BY字段,可以将聚合逻辑拆到子查询中,主查询仅获取用户基础信息,再关联子查询的结果:

return this.qb('user')
  .where({ id })
  .leftJoin('user.wallet', 'wallet')
  .leftJoin('user.userLevel', 'userLevel')
  .select([
    'avatarUrl',
    'otpEnabled',
    'userName',
    'profilePrivacy',
    'userType',
    'userLevel.level',
    'loginType',
    'createdAt',
    'wallet.balance',
    // 子查询计算总发送小费
    (qb) => qb
      .select('sum(transactions.amount)')
      .from('transactions')
      .whereRaw('transactions.user_id = user.id')
      .where('transactions.transactionType.name', TransactionTypeName.TipSent)
      .as('totalTipSent'),
    // 子查询计算总接收小费
    (qb) => qb
      .select('sum(transactions.amount)')
      .from('transactions')
      .whereRaw('transactions.user_id = user.id')
      .where('transactions.transactionType.name', TransactionTypeName.TipReceived)
      .as('totalTipReceived'),
  ])
  .execute('get');

这种方式不需要GROUP BY,因为聚合计算是针对单个用户独立执行的子查询,主查询仅返回单条用户数据,从根源避免了分组限制。

两种方案都能实现你的需求,可根据实际场景选择:方案一写法直接,适合关联表较少的情况;方案二逻辑更清晰,在多关联场景下更易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:46:29