MySQL统计200+公司数据报错:临时表重复键,本地正常服务器异常
这个问题我之前帮同事排查过类似的,咱们先拆解核心原因,再一步步解决:
错误原因分析
你遇到的Can't write; duplicate key in table '...Temp#sql...'错误,本质是MySQL处理你的聚合查询时,需要创建临时表存储中间计算结果,但这个临时表在创建或写入时触发了唯一键冲突。为什么查100家正常、200家就报错?大概率是这两个原因:
- 服务器临时表内存配置不足:当聚合结果超过
tmp_table_size或max_heap_table_size的限制时,MySQL会把内存临时表转成磁盘临时表(通常用MyISAM引擎),磁盘临时表的索引/主键处理逻辑和内存表不同,容易触发重复键问题。 - 手动编写200多个
SUM(IF)列时,可能不小心出现重复别名(哪怕你写的是CY_OC、CY_OC1...也难保手滑),导致临时表列名冲突。
本地环境没问题,是因为本地通常给MySQL分配了更大的临时表内存,不会触发磁盘临时表转换,自然不会暴露这个问题。
解决方案
1. 调整服务器临时表配置
先登录服务器的MySQL,执行以下命令查看当前配置:
SHOW VARIABLES LIKE 'tmp_table_size'; SHOW VARIABLES LIKE 'max_heap_table_size';
如果这两个值很小(比如默认的16M),可以临时调大试试(比如设置为64M):
SET GLOBAL tmp_table_size = 67108864; SET GLOBAL max_heap_table_size = 67108864;
注意:如果服务器内存有限,不要设置过大。想要永久生效,修改MySQL配置文件(my.cnf或my.ini),添加/修改:
tmp_table_size = 64M max_heap_table_size = 64M
然后重启MySQL服务。
2. 重构查询语句(推荐)
手动写200多个SUM(IF)不仅容易出错,还会让查询臃肿。用动态SQL自动生成所有公司的聚合列,既简洁又能避免人为错误:
-- 1. 动态生成聚合列的SQL片段 SET @sql = NULL; SELECT GROUP_CONCAT(DISTINCT CONCAT('SUM(IF(companyID = ''', companyID, ''', CYC, 0)) AS CY_', companyID) ) INTO @sql FROM fntable; -- 2. 拼接完整的查询语句 SET @sql = CONCAT('SELECT ', @sql, ', typeID FROM fntable GROUP BY typeID'); -- 3. 执行动态SQL PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
这个脚本会自动读取fntable里所有companyID,生成对应的SUM(IF)列,完全替代手动写200多行的麻烦。
3. 检查服务器临时目录权限
错误里的临时目录是C:\Windows\SERVIC~2\NETWOR~1\AppData\Local\Temp,确保运行MySQL服务的系统账户对这个目录有读写权限。如果权限不足,临时表写入磁盘时也可能出现异常。
4. 检查MySQL版本差异
对比本地和服务器的MySQL版本:
SELECT VERSION();
如果服务器是较老版本(比如5.6及以下),处理临时表的逻辑可能存在bug,升级到稳定版本(比如5.7或8.0)也可能解决问题。
内容的提问来源于stack exchange,提问作者Shahmee

