CAST(SUM(col) AS BIGINT)无效,因为溢出发生在SUM()内部累加阶段,而非结果输出时;执行顺序是先用原始列类型(如INT)逐行相加,中间值超2147483647即报错退出,CAST根本无机会执行。

为什么CAST(SUM(col) AS BIGINT)根本没用
错误堆栈里看到的 Arithmetic overflow error converting expression to data type int 不是发生在 SELECT 输出阶段,而是 SUM() 内部某次累加就崩了。执行顺序是:先用原始列类型(比如 INT)逐行相加 → 中间值超 2147483647 → 报错退出 → CAST 根本没机会运行。
常见错误现象包括:
- MySQL 返回负数结果(如
SUM(value)得到 -2147483648),而非报错(尤其未开STRICT_TRANS_TABLES时) - SQL Server 直接中断并抛出溢出错误,不返回任何结果
- PostgreSQL 明确提示
integer out of range
关键点在于:溢出不是因为最终结果太大,而是中间累加过程用了太小的类型。别只查 MAX(col),要验证 SUM() 实际用的中间类型——SQL Server 可用 TYPE_NAME(SUM(col)),PostgreSQL 用 pg_typeof(sum(col))。
SUM(CAST(col AS BIGINT)) 是唯一可靠写法
必须把 CAST 放进聚合函数内部,让每一行在参与累加前就升为更大类型。这是跨数据库通用解法,不是“建议”,是强制要求。
使用场景和实操要点:
- 原始列为
INT、SMALLINT或DECIMAL(9,2)时,一律写成SUM(CAST(col AS BIGINT))或SUM(CAST(col AS DECIMAL(18,2))) - 在
HAVING子句中同样适用:HAVING SUM(CAST(salary AS BIGINT)) > 1000000000 - 窗口函数也一样:
SUM(CAST(amount AS BIGINT)) OVER (ORDER BY id),不能写成CAST(SUM(amount) OVER (...)) - JOIN 后聚合更危险:一对多关联会让同一数值被重复读取多次,原本安全的 10 万行可能变成 500 万行,直接触发中间类型上限
LEFT JOIN 后的 amount 求和最容易踩坑
LEFT JOIN orders ON u.id = o.user_id 之后写 SUM(o.amount),表面看只是加个 NULL 安全聚合,实际暗藏三重风险:
-
o.amount为NULL时,部分数据库会隐式收缩类型(如DECIMAL(9,2)→DECIMAL(5,2)) - 若
orders表本身定义为SMALLINT,SQL Server 的SUM()中间类型仍是SMALLINT,不是自动升为INT - 外层
CAST无法覆盖这种隐式推导,必须显式包裹:SUM(CAST(o.amount AS DECIMAL(18,2)))
CTE 或子查询里也别省 CAST:错误堆栈通常只显示最外层语句,嵌套层溢出排查成本极高。
达梦 / MySQL / SQL Server 的中间类型规则差异
没有“自动升级”这回事。各库都按原始列类型硬算中间值,但规则不一致:
- SQL Server:
SMALLINT列的SUM()中间类型仍是SMALLINT;MONEY类型四舍五入规则不透明,建议直接换DECIMAL - MySQL 5.7+:
SUM(INT)默认走 32 位整数计算;未开严格模式时可能静默截断,而非报错 - 达梦 / PostgreSQL:
MAX(block_id * 8192 + bytes)溢出,得写成MAX(CAST(block_id AS DECIMAL) * 8192 + bytes)
真正麻烦的不是不会加 CAST,而是加完上线才发现某些分组的中间类型比预想的还小——比如两个 DECIMAL(9,2) 相加,结果精度可能被压缩,而你只查了 MAX(col),没验证 SUM() 的中间类型。










