ERP对接月报只显示8月和10月,9月没有单据,最近三个月平均值却和这两个月的均值相同。这时先查统计SQL是否只返回"存在记录的月份"。
本文依据实际SQLite统计函数,用构造推送记录复现空月份缺失。样本数量只用于解释计算口径,不代表客户单量、收入或真实收费结果,也没有查询任何真实账套。
1. GROUP BY 不会自动生成没有记录的月份
当前月度统计按连接过滤推送记录,只统计状态为 done 的行,核心SQL如下:
sql
SELECT strftime(
'%Y-%m',
datetime(updated_at / 1000, 'unixepoch', '+8 hours')
) AS ym,
doc_type, COUNT(*) AS n
FROM push_records
WHERE conn_id = ? AND state = 'done'
GROUP BY ym, doc_type
ORDER BY ym DESC;
月份由记录的 updated_at 计算,按代码中的UTC加8小时归月;不是按来源单据的业务日期归月。一个月没有满足条件的行,就没有该月分组。
查询之后,代码按月份合并不同单据类型的数量,再对结果执行 slice(0, months)。因此这里的 months 限制的是"返回多少个有记录的月份",没有创建连续的自然月区间。
2. 最近三个月为什么只用了两个数
在内存数据库中构造12条 done 记录:2026年8月6条,10月6条,9月0条。单据号和连接标识全部为演示值,调用实际 monthlyCounts(..., { months: 3 }) 得到:
json
[
{"ym":"2026-10","total":6},
{"ym":"2026-08","total":6}
]
这里展示月份和总量,实际返回还包含按单据类型汇总的 byType。随后平均值函数执行:
javascript
const take = list.slice(0, months);
if (!take.length) return 0;
return Math.round(
take.reduce((s, m) => s + m.total, 0) / take.length
);
它使用实际取到的列表长度作分母。本次得到 (6 + 6) / 2 = 6,而不是把9月补0后的 (6 + 0 + 6) / 3 = 4。

图1:内存数据库构造数据;9月为0是本例已知条件,不是对生产缺失月份的推断。
这不是SQLite把0算错了,而是输入给平均函数的列表根本没有9月。
3. 先决定月报要表达哪一种口径
"最近三个有业务的月份"和"截至某月的连续三个月"都可以是有效指标,但含义不同。若页面或合同写连续自然月,就不能仅截取三个分组充当实现。
还要明确是否包含本月。本月尚未结束,和前两个月直接比较可能造成误读;应约定统计截止日,或者只取完整自然月。
对新接入的连接,也要明确启用前的月份如何处理。没有接入、没有完整采集和确实没有业务,不能一概记成0。
本地构造证明了分母差异,没有证明任何客户应使用哪个口径。实际验收应先把这些定义写清,再检查代码与页面说明是否一致。
4. 需要连续月份时,先建立月份序列
一种建议做法是先固定统计截止月,再生成连续月份,随后按月份合并聚合结果。下面的独立片段只演示合并,月份序列是手工给定的构造值:
javascript
const months = ['2026-10', '2026-09', '2026-08'];
const counts = new Map([
['2026-10', 6],
['2026-08', 6]
]);
const filled = months.map(ym => ({
ym,
total: counts.get(ym) ?? 0
}));
本例已知9月没有业务,因此可以补0,再交给平均函数计算,得到4。生产使用时应先验证该月的采集完整性;未知或采集失败可以单独标记,不能静默补0并宣称准确月均。
该建议没有修改当前统计实现,也没有实现通用月份生成器。跨年、月末、闰年及不同统计窗口,还需要单独验证。
5. 月份补齐后,记录归属也要核对
当前SQL同时依赖记录的现有状态和 updated_at。它统计的是此刻满足 done 条件的记录,不是不可变的成功事件流水;后续状态或时间字段变更可能改变重新查询的结果。
如果业务要求历史月报在结账后保持稳定,应考虑独立成功时间、状态事件或已经确认的月度快照。这些是设计方向,没有在本次实现或验证为完整报表方案。
验收时建议同时覆盖:中间月为0、所有月为0、只有一个月、跨年窗口、新连接启用月,以及记录时间和单据业务日期跨月。展示数量、平均分母和截止时间,让读者能还原计算过程。
本次实际验证了两个事实:没有记录的月份不出现在当前分组结果中,平均函数按返回列表长度计算。连续自然月补0后的4是本地演示对照值,不能替代真实业务的完整性核实。