溢出发生在聚合中间累加阶段,而非结果赋值时;必须用sum(cast(col as bigint))将类型提升提前至每行参与计算前,而非cast(sum(col) as bigint),因后者在sum执行完溢出后才转换,已无效。

不是数据太大,而是数据库在累加过程中用错了中间类型——SUM()执行时默认按原始列类型做中间计算,溢出发生在加总途中,外层转换根本来不及介入。
溢出发生在聚合计算的中间阶段,而非结果赋值时
比如 INT 列求和,SQL Server 和 MySQL 都不会自动把中间累加器升级为 BIGINT;哪怕最终目标字段是 DECIMAL(18,2),只要原始列是 INT,加到第 2147483648 就直接报错。错误堆栈里看到的 “Arithmetic overflow” 不是你 SELECT 的结果超限,而是 SUM 过程中某次累加已经炸了。
-
CAST(SUM(col) AS BIGINT)是无效写法:SUM 先按col类型算,溢出后 CAST 根本不执行 - 正确写法必须是
SUM(CAST(col AS BIGINT)):把类型提升提前到每行参与累加前 - 用
TYPE_NAME(SUM(col))(SQL Server)或pg_typeof(sum(col))(PostgreSQL)验证实际中间类型,别只看MAX(col)
JOIN 后直接 SUM 会放大溢出风险
一对多关联(如订单→明细)会让同一数值被重复读取多次,SUM 不仅可能虚高,还可能因行数暴增导致中间累加次数远超预期——原本 10 万行 INT 求和安全,JOIN 后变成 500 万行,就容易触到中间类型的上限。
- 别在 JOIN 后直接
SUM(o.amount),尤其当o.amount是SMALLINT或DECIMAL(9,2)时 - 先用 CTE 或子查询对明细表预聚合:
SUM(CAST(amount AS DECIMAL(18,2))),再和主表 LEFT JOIN - 如果明细列含 NULL,
SUM()会忽略它,但隐式类型推导可能收缩精度,务必显式 CAST
不同数据库对中间类型的处理差异极大
MySQL 5.7+ 默认用 32 位整数做 SUM(INT) 中间计算,且不开 STRICT_TRANS_TABLES 时可能静默截断;SQL Server 对 SMALLINT 列的 SUM() 中间类型仍是 SMALLINT,不是自动升为 INT;达梦和 PostgreSQL 同样不会自动拓宽,MAX(block_id * 8192 + bytes) 溢出就得写成 MAX(CAST(block_id AS DECIMAL) * 8192 + bytes)。
- 不要假设“数据库会聪明地选更大类型”,它基本不会
-
MONEY类型尽量换掉:四舍五入规则不透明,中间步骤就可能失真 - DECIMAL 相加时,精度和小数位可能被压缩,两个
DECIMAL(9,2)相加结果可能是DECIMAL(10,2),但SUM()可能只给DECIMAL(9,2),得手动指定宽度
最麻烦的不是加 CAST,而是加完上线才发现某些分组的中间类型比预想的还小——比如你查了 MAX(amount) 是 999999.99,就以为 SUM() 安全,却没验证两个 DECIMAL(9,2) 累加时数据库到底用了什么精度做中间存储。











