如何在findAll()的literal查询中绑定/访问当前行数据,兼容MySQL 5.7
问题根因
这个报错是MySQL 5.7的固有语法限制导致的:位于FROM子句中的派生表(也就是你代码里第二层SELECT生成的临时表)无法引用外层查询的表字段,该限制在MySQL 8.0.14版本才被解除,所以高版本运行正常,5.7版本报错。
可行解决方案
方案1:调整SQL写法,去掉派生表(推荐)
如果你是要计算每个账户最近12个月的用电量平均,直接去掉内层的派生表即可,调整后的literal写法如下:
[literal(`( SELECT AVG(kwh_used) FROM consumer_bill cb WHERE cb.account_no_id = MeterReading.account_no_id AND cutoff_month >= DATE_SUB(billing_month, INTERVAL 12 MONTH) )`), 'avg']
如果你确实需要按条数取最新12条记录的平均(不是按自然月),可以用GROUP_CONCAT配合字符串拆分实现,避开派生表:
[literal(`( SELECT AVG(SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(kwh_used ORDER BY cutoff_month DESC SEPARATOR ','), ',', n), ',', -1)) FROM consumer_bill cb JOIN (SELECT 1 n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) num WHERE cb.account_no_id = MeterReading.account_no_id HAVING n <= COUNT(*) )`), 'avg']
方案2:使用Sequelize字段占位符确保列引用正确
如果调整写法后还是提示未知列,可以用Sequelize内置的$表名.字段$占位符替换手动写的列名,Sequelize会自动解析为正确的带别名的列引用:
[literal(`( SELECT AVG(kwh_used) FROM consumer_bill cb WHERE cb.account_no_id = $MeterReading.account_no_id$ AND cutoff_month >= DATE_SUB(billing_month, INTERVAL 12 MONTH) )`), 'avg']
方案3:分两步查询(适用于数据量小的场景)
如果数据量不大,可以先查询所有MeterReading记录,再循环每条记录单独查询对应的平均用电量,最后把值拼接回去:
// 第一步先查主表数据 const mr = await req.db.models.MeterReading.findAll({ where: { billing_month }, include: [ { model: req.db.models.Consumer, as: 'consumer', where: { route_no }, required: true, } ] }); // 第二步循环补全avg字段 for (const row of mr) { const avgResult = await req.db.models.ConsumerBill.findOne({ attributes: [[sequelize.fn('AVG', sequelize.col('kwh_used')), 'avg']], where: { account_no_id: row.account_no_id }, order: [['cutoff_month', 'DESC']], limit: 12, raw: true }); row.setDataValue('avg', avgResult.avg); }
内容的提问来源于stack exchange,提问作者Julius Guevarra
相关产品推荐
相关产品推荐

