
“过去三天日均金额”看上去是一个简单需求。如果表里不是每天都有记录,最后三行和最后三个自然日就会给出不同答案。模型把窗口函数写得很熟练,也不会替业务补上这个定义。

原理示意,非真实系统截图或性能测量。
一张能手算的小账
教学数据只有三笔日汇总:10月1日10元、3日30元、4日40元。要求4日往前含当天三天,即2、3、4日;假设没有记录表示当天金额为零,答案为70/3。取最后三行得到80/3,范围变成1、3、4日。
from datetime import date,timedelta
from fractions import Fraction
daily={date(2026,10,1):10,date(2026,10,3):30,date(2026,10,4):40}
end=date(2026,10,4);days=3
calendar=[end-timedelta(days=i) for i in range(days)]
calendar_average=Fraction(sum(daily.get(d,0) for d in calendar),days)
last_rows=sorted(daily.items())[-days:]
row_average=Fraction(sum(v for _,v in last_rows),len(last_rows))
assert calendar_average==Fraction(70,3)
assert row_average==Fraction(80,3)
assert calendar_average!=row_average
print("calendar:",calendar_average,"last rows:",row_average)
本地Python运行得到这两个分数。日期和金额都是合成输入,不是某次财务系统的结果。
NULL和零,先别急着互换
刚才“缺日补零”是明确假设。如果缺记录表示采集未完成,就不能补零;应该返回数据不完整,或者携带watermark。否则系统会把“还不知道”写成“当天没发生”。
SQL实现可选择先补日历表,再按固定日粒度使用ROWS;也可以使用目标数据库支持的时间RANGE。但“前30天加当前日”的端点可能覆盖31个日历日期。时区转换、自然日截断和区间开闭都应先写成业务约定。
同一时间戳下多笔记录还需要稳定排序键。若累计计算依赖先后顺序,只有ORDER BY ts而ts并不唯一,重复运行或分布式归并可能改变中间结果。
类型正确也不保证口径正确
MySQL Precision Math文档规定精确数值、舍入和SQL mode行为。金额应明确币种精度与舍入阶段,不应把每个中间值都先转成DECIMAL(18,2)。两个先舍入再相加,与相加后再舍入,可能差一分钱。
PremiseSQL研究提醒我们,问题前提不全时,直接生成SQL可能制造看似合理的答案。这里适合先追问“自然日还是交易日”“缺失意味着零还是未知”。
这次验证了日历与行窗口的有限反例,未连接MySQL/ClickHouse,未让模型生成财务报表。跨引擎窗口语法和真实业务金标仍需单独核验;解析成功、金额类型合法、财务含义正确,是三张不同的验收单。





