SQL中SUM溢出需在聚合前显式CAST字段为BIGINT等大类型,而非对SUM结果转换;因溢出发生在内部计算阶段,CAST(SUM(col) AS BIGINT)无效。

SQL中SUM结果超出INT范围直接报错
当表中某数值字段是INT类型(比如MySQL的INT SIGNED最大值为2147483647),而聚合后总和超过该上限,SUM()会触发整数溢出——MySQL默认不自动提升类型,直接报错ERROR 1690 (22003): BIGINT UNSIGNED value is out of range(或类似溢出提示)。这不是数据问题,是类型计算链路没对齐。
- 别指望
SUM(col)自己“聪明”升到BIGINT,它严格按输入列类型推导返回类型 - 即使你用
CAST(SUM(col) AS BIGINT)也晚了——溢出发生在SUM()内部计算阶段,强制转换只是对已溢出的结果再转,无效 - 必须在求和前把参与运算的字段显式转成更大容量类型
正确做法:先CAST字段,再SUM
核心是打断原始类型的计算路径。把原始INT字段在进入SUM()前就转成BIGINT,让整个加法运算在64位空间里进行。
MySQL写法:
SELECT SUM(CAST(amount AS SIGNED)) FROM orders;
PostgreSQL写法:
SELECT SUM(amount::BIGINT) FROM orders;
SQL Server写法:
SELECT SUM(CAST(amount AS BIGINT)) FROM orders;
-
CAST(amount AS SIGNED)在MySQL中等价于CAST(amount AS BIGINT),因为SIGNED默认就是BIGINT精度(注意:不是所有版本都支持SIGNED作目标类型,稳妥起见用BIGINT) - 别写
CAST(SUM(amount) AS BIGINT)——这无法避免中间溢出 - 如果字段本身是
UNSIGNED INT,要转UNSIGNED BIGINT,否则负号可能引发隐式转换异常
为什么不能只靠修改字段定义?
有人想一劳永逸:把源字段类型直接改成BIGINT。这在部分场景可行,但有现实约束:
- 已有大量应用代码依赖
INT语义(比如前端做范围校验、ORM映射配置),改库结构成本高 - 某些云数据库(如阿里云PolarDB MySQL版)对在线DDL加锁严格,大表改类型可能阻塞业务数小时
- 如果只是临时报表查询或ETL中间步骤,没必要动基表结构
-
BIGINT比INT多占4字节,千万级以上表会显著增加索引体积和内存占用
额外注意:NULL和DECIMAL字段的处理差异
CAST对不同源类型行为不一致,容易踩坑:
-
INT→BIGINT:安全无损,直接转 -
DECIMAL(10,2)→BIGINT:会截断小数部分,若需保留精度,应转DECIMAL(20,2)再SUM - 含
NULL字段:CAST(NULL AS BIGINT)仍是NULL,不影响SUM逻辑(SUM天然忽略NULL) - 某些老版本MySQL(如5.6)对
CAST(x AS SIGNED)在聚合中偶发优化失效,建议升级或显式用BIGINT
最常被忽略的一点:溢出不一定报错——有些数据库(如旧版SQL Server)会静默回绕,导致结果错误但无提示。所以一旦怀疑总量较大,别等报错,先预判类型容量。

















