DEBUG.WIKI
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
阿里云;从建站到云上业务;云服务器 · 数据库 · 对象存储;查看产品 →
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

SQLite SUM 已转为 BIGINT,仍报 integer overflow

SQLite BIGINT 不扩大 SUM 的整数累加范围。区分允许近似的 REAL 聚合与精确大整数求和,保留原始数值并核对空集合及中间溢出。

SUM(CAST(amount AS BIGINT)) 在数据少时正常,多几条记录就报 integer overflow。把列从文本改成整数也没有解决:每条数值都能存下,但累加结果超出了 SQLite 的有符号 64 位整数范围。

SQLite 的 BIGINT 不提供比 INTEGER 更宽的整数累加器。下文处理普通 SQLite 数值聚合,先决定结果是否必须精确,再选查询方式;不需要为了排错直接改写原始列。

一条记录能存下,不代表总和也能存下

SQLite 整数范围是 -9223372036854775808 到 9223372036854775807。例如十条 1000000000000000000 分别都在范围内,总和却已超过上限。

先核对实际查询范围和少量值的存储类型:

SELECT amount, typeof(amount)
FROM ledger
LIMIT 10;

ledger、amount 是示例名称。若查询存在连接,应同时检查连接是否把同一条记录复制了多次;多算数据与正确总和过大,需要不同处理。文本列也要核对内容,不能把任意字符串强制转换后就认为数据合法。

对于只包含整数或 NULL 的输入,sum() 在计算过程中的任意一步溢出都可以报错。这意味着正负数最终可能抵消,也不能据此保证中间累加安全;不要靠改变读取顺序碰运气。

CAST(... AS BIGINT) 仍使用整数亲和性。把类型名换成 DECIMAL 或 NUMERIC 也不会凭空引入任意精度十进制运算。

允许近似结果时,把输入转为 REAL

如果用途允许浮点近似,可以在聚合之前转换每个输入:

SELECT sum(CAST(amount AS REAL)) AS approximate_total
FROM ledger;

也可以使用始终返回浮点值的 total():

SELECT total(amount) AS approximate_total
FROM ledger;

它们可以避开整数累加溢出,但不能承诺大整数的每一位都准确。尤其金额、结算数量或需要逐位一致的统计,不应仅因查询不报错就接受浮点结果。

转换的位置不能放到外面:CAST(sum(amount) AS REAL) 要先算完整数总和,内层已经溢出时,外层转换没有机会补救。也不要只向数据中临时塞入一个浮点值来影响聚合类型。

两种函数的空集合行为也不同:没有任何非 NULL 输入时,sum() 返回 NULL,total() 返回 0.0。替换前确认下游是否用 NULL 表示没有数据。

必须精确时,保留原值并采用精确累加

REAL 使用 binary64 浮点表示,扩大可表示范围的同时会引入精度限制。把一个已经损失精度的浮点结果再转成文本或整数,不能恢复原来的数字。

需要超出 64 位范围的精确总和时,可在应用层使用支持任意精度整数或十进制的计算方式,按原始文本严格解析后累加;或者使用已经由应用批准并集成的 SQLite decimal 扩展。

在确认当前连接已经提供该扩展、输入是经过校验的十进制数字时,查询形式为:

SELECT decimal_sum(amount) AS exact_total
FROM ledger;

decimal_sum() 不是 SQLite 核心默认提供的函数。出现 no such function 表示当前连接没有该功能,不能通过给列改名或随意从网上下载扩展来修复。扩展的部署、可信来源和连接加载方式,应按应用原有机制单独核定。

如果单条原始值已经超过 64 位范围,进入精确累加之前更不能先 CAST AS INTEGER。保留原始数字文本与校验结果,避免在输入阶段就丢失信息。

验收要比较数值与空值语义

使用已知精确答案的小样本,分别覆盖普通整数总和、超过 64 位的总和、空集合及正负抵消。近似路线要有业务允许的误差标准;精确路线应逐位核对结果,并确认输出驱动和下游没有再次转成有限精度浮点数。

以上 SQL 只读取数据。保留原查询便于回退,不要先把原始列批量更新成 REAL。若过去已经保存了近似汇总,应从保留的原始数据重新计算;恢复查询文字本身不会修复历史汇总值。