cast必须包在sum()内,如sum(cast(salary as bigint)),否则中间累加仍用原始小类型溢出;having、join结果及cte均需同步处理,分组爆炸时应主键分片而非调大内存。

CAST必须包在SUM()里面,不能包在外面
溢出不是数据太大,而是加法运算卡在了原始列的小类型里。比如salary是INT,数据库就用32位空间做累加,中间值一超限(比如变成-2147483648),再CAST(SUM(salary) AS BIGINT)也救不回来。
正确写法只有一种:SUM(CAST(salary AS BIGINT))。这会让每行salary先转成64位,再进加法器——整个路径都在大类型空间里跑。
- MySQL 5.7+、PostgreSQL、达梦、SQL Server 全部遵循这个规则,无例外
-
HAVING子句里也要同步改:HAVING SUM(CAST(salary AS BIGINT)) > 1000000000,否则过滤阶段照样溢出 - 如果字段来自
LEFT JOIN结果,注意NULL可能触发隐式类型重推导,建议显式补COALESCE(col, 0)再CAST
GROUP BY分组数爆炸时,别硬调work_mem或tmp_table_size
PostgreSQL的work_mem设到1GB,MySQL的tmp_table_size拉到2GB,都救不了分组键超千万或单个user_id对应500万行的场景——哈希表内存占用是线性增长的,不是阈值问题。
真正可控的做法是主动拆解:用主键范围切片,走索引聚合。
- 先建临时表:
CREATE TEMPORARY TABLE tmp_ids (id BIGINT PRIMARY KEY) - 每次插5万以内ID:
INSERT INTO tmp_ids VALUES (100001), ..., (150000) - 聚合时JOIN该表:
SELECT user_id, COUNT(*) FROM orders JOIN tmp_ids USING (id) GROUP BY user_id - 这样每次只处理几万行,内存峰值稳定在50–200MB,且可并行跑多批次
字符串类聚合报“too long”错误,大概率不是字段长度问题
直接ALTER TABLE ... MODIFY COLUMN desc_text VARCHAR(10000)往往无效。报错根源通常在GROUP BY desc_text构建哈希键时,或STRING_AGG(desc_text, ';')内部缓冲区撑爆,和存储定义无关。
先用执行计划定位:
- PostgreSQL执行
EXPLAIN (ANALYZE, BUFFERS),看到Hash Key或Sort Method: external merge Disk,说明是计算过程溢出 - MySQL查
MAX(LENGTH(desc_text)),如果结果才300,却报错,那加长字段纯属白忙 - 真要保留全文,先
STRING_AGG(COALESCE(desc_text, ''), ';'),再全局调大group_concat_max_len(MySQL)或用ARRAY_AGG()(PG)
客户端取聚合结果时OOM,和数据库配置无关
JDBC驱动默认把整个结果集load进JVM堆,哪怕数据库端已算完,ResultSet一打开就崩——这不是数据库内存不够,是客户端没流式读。
- MySQL连接串必须加:
?useCursorFetch=true&defaultFetchSize=500 - PostgreSQL需显式声明游标:
DECLARE agg_cursor CURSOR FOR SELECT ... GROUP BY ...,再FETCH 500 - 注意:
defaultFetchSize对INSERT ... SELECT或CTE聚合无效,这类语句服务端仍要全量计算
最易被忽略的是:CTE默认不物化,WITH t AS (SELECT ...) SELECT COUNT(*) FROM t GROUP BY x依然可能OOM,MySQL 8.0+得加MATERIALIZED提示,PG得用MATERIALIZED或临时表兜底。











