必须在SUM()内部对字段CAST,因为溢出发生在中间累加阶段,外层CAST无法挽救;正确写法是SUM(CAST(col AS BIGINT)),错误写法是CAST(SUM(col) AS BIGINT)。

直接在 SUM() 内部对字段做 CAST,而不是对 SUM() 结果再转——否则溢出早已发生,补救无效。
为什么 CAST(SUM(col) AS BIGINT) 一定失败
数据库执行顺序是先算 SUM(col),再套外层 CAST。如果 col 是 INT,中间累加全程用 32 位空间,一旦超过 2147483647,MySQL 报 ERROR 1690 (22003),SQL Server 报“算术溢出”,PostgreSQL 报 integer out of range——CAST 根本没机会运行。
常见错误现象:
- 查询在本地小数据集上正常,上线后大表突然报错
- 开了
STRICT_TRANS_TABLES就报错,关了就静默截断成 2147483647,结果错得更隐蔽 - CTE 或子查询里漏写
CAST,错误堆栈只显示最外层语句,定位困难
SUM(CAST(col AS BIGINT)) 的实操要点
必须把 CAST 包在聚合函数最内层,让整个加法过程在 64 位空间进行。不同数据库写法略有差异:
- MySQL:
SUM(CAST(amount AS SIGNED))或更稳妥的SUM(CAST(amount AS BIGINT)) - PostgreSQL:
SUM(amount::BIGINT)或SUM(CAST(amount AS BIGINT)) - SQL Server:
SUM(CAST(amount AS BIGINT))
注意边界情况:
- 若源字段是
UNSIGNED INT,应转UNSIGNED BIGINT,否则负号可能引发隐式转换异常 -
DECIMAL(10,2)转BIGINT会丢小数;需保留精度时,改用DECIMAL(20,2) -
NULL值不影响:CAST(NULL AS BIGINT)仍是NULL,而SUM()天然忽略NULL
HAVING、JOIN、窗口函数里也得同步处理
类型问题不只出现在 SELECT 列表,所有涉及该字段的计算节点都可能触发独立的类型推导:
-
HAVING SUM(CAST(salary AS BIGINT)) > 1000000000✅;HAVING CAST(SUM(salary) AS BIGINT) > 1000000000❌ -
LEFT JOIN后取o.amount做聚合,因NULL可能导致隐式类型重推,建议先COALESCE(o.amount, 0)再CAST - 窗口函数如
SUM(CAST(amount AS BIGINT)) OVER (ORDER BY created_at, id),ORDER BY必须显式且含唯一性列,避免排序不确定性
别指望数据库自动升维——SQL Server 对 SMALLINT 列求和,中间类型仍是 SMALLINT;MySQL 5.7+ 对 INT 求和,中间仍是 32 位;达梦、PostgreSQL 同理。
上线前容易被忽略的验证动作
真正难的不是加一行 CAST,而是确认它生效在每一个计算路径上:
- SQL Server 用
TYPE_NAME(SUM(col))和PRECISION(SUM(col))查中间结果类型,别只看MAX(col) - MySQL 8.0+ 可用
EXPLAIN FORMAT=TRADITIONAL观察是否触发隐式转换 - 存储过程中,
DECLARE @total DECIMAL(12,2)必须匹配目标字段精度,赋值时仍要CAST(SUM(CAST(col AS DECIMAL(12,2))) AS DECIMAL(12,2)) - 两个
DECIMAL(9,2)相加,结果精度可能变成DECIMAL(19,2),而你只查了单值,没验证聚合中间类型
最麻烦的不是不会写 CAST,而是加完上线才发现某个 HAVING 条件或某次 JOIN 后的聚合,依然在用原始类型默默计算。

















