如何在Sequelize中对子关联的虚拟列求和?
解决方案
核心问题原因
totalDuration是C模型的虚拟列,仅在Sequelize实例层面计算,数据库中不存在该字段,因此无法直接用它做SQL聚合(比如SUM)。必须直接基于数据库中实际存在的xDuration和yDuration字段进行计算。
1. 统计B的总时长
修改查询逻辑,直接在SQL层面计算每个C的xDuration + yDuration并求和:
const B = require('./B.model'); const C = require('./C.model'); const { sequelize, fn, literal } = require('sequelize'); B.findAll({ attributes: [ 'id', 'AId', // 保留B自身需要的字段 [fn('SUM', literal('"C"."xDuration" + "C"."yDuration"')), 'totalDuration'] ], include: [{ model: C, attributes: [], // 无需返回C的字段,仅用于关联计算 required: true // 可选:只返回关联了C的B }], group: ['B.id', 'B.AId'] // 按B的主键分组,避免分组逻辑错误 });
也可以拆分求和逻辑,效果完全一致:
attributes: [ 'id', 'AId', [fn('SUM', col('C.xDuration')), 'sumX'], [fn('SUM', col('C.yDuration')), 'sumY'], [literal('SUM("C"."xDuration") + SUM("C"."yDuration")'), 'totalDuration'] ]
2. 统计A的总时长
A的总时长是其所有关联B下的C的时长总和,需要通过两层关联完成聚合:
const A = require('./A.model'); const B = require('./B.model'); const C = require('./C.model'); const { fn, literal } = require('sequelize'); A.findAll({ attributes: [ 'id', [fn('SUM', literal('"B->C"."xDuration" + "B->C"."yDuration"')), 'totalDuration'] ], include: [{ model: B, include: [{ model: C, attributes: [] }], attributes: [] }], group: ['A.id'] });
3. 模型层面添加实例方法(可选)
如果希望在获取实例后直接拿到总时长,可给B和A添加实例方法,在内存中计算总和(适合数据量较小的场景):
给B模型添加实例方法
修改B.model.js:
B.prototype.getTotalDuration = async function() { const Cs = await this.getCs(); // 利用Sequelize自动生成的关联查询方法 return Cs.reduce((sum, c) => sum + c.totalDuration, 0); };
使用方式:
const b = await B.findByPk('b_id', { include: C }); const total = await b.getTotalDuration();
给A模型添加实例方法
修改A.model.js:
A.prototype.getTotalDuration = async function() { const Bs = await this.getBs({ include: C }); return Bs.reduce((sum, b) => { const bTotal = b.Cs.reduce((bSum, c) => bSum + c.totalDuration, 0); return sum + bTotal; }, 0); };
内容的提问来源于stack exchange,提问作者Lucas Pereira
相关产品推荐
相关产品推荐

