必须在sum()内部对字段做cast,因为溢出发生在累加过程中,外层cast无法阻止;如sum(cast(col as bigint))确保全程64位运算,而cast(sum(col) as bigint)会因32位中间结果溢出失败。

必须在 SUM() 内部对字段做 CAST,而不是对结果再转——溢出发生在累加过程中,外层转换根本来不及介入。
为什么 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)) 的实操要点
目标是让整个加法过程在 64 位空间进行,不是“看起来转了就行”。
- MySQL:用
SUM(CAST(amount AS BIGINT))(SIGNED在部分版本行为不一致,不推荐) - PostgreSQL:支持
SUM(amount::BIGINT)或SUM(CAST(amount AS BIGINT)) - SQL Server:必须写
SUM(CAST(amount AS BIGINT)),CONVERT也行,但语义不如CAST清晰 - 若源字段是
UNSIGNED INT,应转UNSIGNED BIGINT,否则负号可能引发隐式转换异常 -
DECIMAL(10,2)转BIGINT会丢小数;需保留精度时,改用DECIMAL(20,2) -
NULL值不影响:CAST(NULL AS BIGINT)仍是NULL,而SUM()天然忽略NULL
所有涉及该字段的计算节点都得同步处理
类型问题不只出现在 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必须显式且含唯一性列,避免排序不确定性 - 存储过程中赋值:不能只声明
DECLARE @total DECIMAL(18,2),还必须写SELECT @total = CAST(SUM(CAST(amount AS DECIMAL(18,2))) AS DECIMAL(18,2))
真正难的不是加一行 CAST,而是确认它生效在每一个计算路径上——包括 GROUP BY 表达式、JOIN 条件、变量接收、甚至窗口帧内运算。漏掉任意一个,溢出就还在那里等着你。










