移动平均线在SQL里的等价逻辑是使用AVG()配合OVER()窗口函数与ROWS BETWEEN子句计算指定行数的滑动均值,如ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING取当前及前后各1行共3行均值,必须显式ORDER BY确保行序稳定,禁用RANGE以防重复值导致窗口漂移。

什么是移动平均线在SQL里的等价逻辑
移动平均线本质是窗口内数据的均值,SQL里不能直接写“过去N天平均”,必须用窗口函数或自连接模拟滑动窗口。子查询只是其中一种写法,但性能差、可读性低,AVG() OVER() 才是现代SQL的标准解法。
用子查询实现3日移动平均的典型错误写法
常见错误是用相关子查询对每行查前3条记录,但忽略日期连续性、重复值、NULL处理,导致结果错位或报错:
- 没按时间排序就用
TOP 3(SQL Server)或LIMIT 3(PostgreSQL/MySQL),顺序不可控 - 子查询中没加
WHERE date ,漏掉“截止当前”的约束 - 没处理同一天多条记录的情况,导致重复计数或漏行
- 用
AVG(price)直接包裹子查询,但子查询返回多行会报错:Subquery returns more than 1 row
正确写法必须用聚合 + 子查询组合,例如:
SELECT t1.date, t1.price, (SELECT AVG(t2.price) FROM stock_data t2 WHERE t2.date BETWEEN DATE_SUB(t1.date, INTERVAL 2 DAY) AND t1.date) AS ma3 FROM stock_data t1 ORDER BY t1.date;
为什么子查询版MA3在大数据量下会变慢
每行触发一次独立子查询,时间复杂度接近 O(n²),且无法利用索引加速范围扫描。当表有10万行时,实际执行约10亿次行扫描。
- 必须给
date字段建索引,否则子查询全表扫 - MySQL 5.7及更早不支持在子查询中使用
ORDER BY ... LIMIT配合聚合,会报语法错 - PostgreSQL允许,但 planner 可能选错执行计划,需加
EXPLAIN ANALYZE验证 - 如果日期有空缺(如周末无交易),
INTERVAL 2 DAY会跳过真实交易日,算出的不是“最近3个交易日”而是“最近3个日历日”
真正该用什么替代嵌套子查询
所有主流SQL引擎都支持窗口函数,AVG() OVER() 是唯一合理选择:
SELECT date, price,
AVG(price) OVER (
ORDER BY date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS ma3
FROM stock_data;
注意点:
-
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW表示包含当前行和它前面2行,共3行 —— 这才是“3日”的准确含义 - 如果想按交易日而非物理行序计算,先用
DENSE_RANK() OVER (ORDER BY date)生成序列号再窗口,避免日期空缺干扰 - SQLite 3.25+、MySQL 8.0+、PostgreSQL 9.4+、SQL Server 2005+ 均支持;旧版本才被迫用子查询
嵌套子查询不是错,而是信号:说明你正在绕开SQL本应提供的能力。真要兼容老系统,优先考虑CTE+ROW_NUMBER(),而不是层层嵌套。

















