必须在聚合函数内部做cast,即sum(cast(col as bigint)),而非cast(sum(col) as bigint),因为溢出发生在中间累加阶段,数据库按原始列类型(如int)进行计算,超2147483647即报错或截断,外层cast无法挽救已发生的溢出。

溢出不是数据太大,而是数据库在加总过程中用错了中间类型——SUM()默认按原始列类型累加,INT列求和中间仍用INT,超2147483647就炸,跟最终目标字段类型无关。
为什么CAST(SUM(col) AS BIGINT)根本没用
错误写法看似合理,但执行顺序是先算SUM(col)(用原始列类型),再转换。如果col是INT,加总过程已溢出报错,CAST压根没机会执行。
- ✅ 正确写法:
SUM(CAST(col AS BIGINT)) - ❌ 错误写法:
CAST(SUM(col) AS BIGINT) - SQL Server、MySQL 5.7+、PostgreSQL、达梦都遵循这一规则——中间类型只看输入列,不看外层包装
- 用
TYPE_NAME(SUM(col))和PRECISION(SUM(col))查实际中间类型,别只看MAX(col)
HAVING和WHERE里也得同步改类型
过滤条件中的表达式同样走原始类型路径,光改SELECT不够。一旦HAVING SUM(col) > 1000000000,而col是INT,比较前的加总已经崩了。
- ✅
HAVING SUM(CAST(salary AS BIGINT)) > 1000000000 - ❌
HAVING CAST(SUM(salary) AS BIGINT) > 1000000000 - JOIN后聚合更危险:LEFT JOIN可能引入
NULL,触发隐式类型重推,建议先COALESCE(col, 0)再CAST - CTE或子查询里漏掉
CAST,错误堆栈常只显示最外层语句,排查成本陡增
窗口函数SUM() OVER也逃不开类型陷阱
SUM() OVER本身不溢出,但输入列类型不对,中间累计照样崩。尤其在MySQL 8.0+严格模式下,直接中断查询;PostgreSQL会报integer out of range。
- ✅
SUM(CAST(amount AS BIGINT)) OVER (ORDER BY created_at, id) - ❌
CAST(SUM(amount) AS BIGINT) OVER (...) -
ORDER BY必须显式写,否则累计逻辑不可靠;时间相同要加唯一列(如id)避免排序不确定性 - 慎用
RANGE:值重复时会合并多行,导致累计“跳变”,且多数数据库无法高效索引
真正难的不是写对那行CAST,而是意识到所有涉及该字段的计算节点——包括GROUP BY键、HAVING、变量赋值、窗口帧内运算、甚至JOIN后的隐式类型重推——都可能各自触发一次类型推导。漏掉任意一个,溢出就还在那里等着你。










