临时表必须在游标声明前创建且不能被动态sql引用;应避免逐行insert,改用批量操作;需显式drop临时表防重定义;变量名须与字段名隔离;必须设置not found处理器。

临时表必须在游标前显式创建,且不能用动态SQL引用
MySQL中,CREATE TEMPORARY TABLE语句必须写在游标声明(DECLARE CURSOR)之前,且整个存储过程内所有静态SQL(如INSERT INTO tmp SELECT …、SELECT * FROM tmp)都能正常访问它。但一旦进入PREPARE/EXECUTE流程,哪怕只是SET @sql = 'SELECT * FROM tmp'; EXECUTE stmt;,就会报错 Table 'tmp' doesn't exist——这不是权限或拼写问题,是MySQL解析器对动态SQL强制隔离作用域的硬限制。
常见错误现象:
- 在游标循环里反复执行
PREPARE stmt FROM @sql并试图查临时表,执行时报错 - 把临时表名拼进字符串再
EXECUTE,例如SET @sql = CONCAT('INSERT INTO ', tmp_name, ' VALUES (?)');,语法通过但运行失败
游标循环插入临时表时,优先用批量INSERT代替逐行INSERT
很多人习惯在游标FETCH后立刻INSERT INTO tmp VALUES (@a, @b),这在数据量稍大时会明显变慢:每条INSERT都触发日志写入、锁竞争和引擎层开销。而MySQL原生支持集合操作,效率高得多。
实操建议:
- 如果游标结果集本身可静态描述(比如固定来自某张表或JOIN),直接用
INSERT INTO tmp_table SELECT id, name, age FROM person WHERE ... - 若必须分步计算(如先算中间聚合,再关联扩展),把各步结果存入不同临时表,最后
JOIN或UNION组合 - 真要逐行处理(如调用函数、条件分支复杂),至少把多行攒成一批再
INSERT,避免单行提交
临时表生命周期管理:每次调用都要考虑IF NOT EXISTS和显式DROP
临时表只在当前会话可见,但不会因存储过程结束自动销毁——它存活到连接关闭为止。这意味着同一连接多次调用该存储过程时,CREATE TEMPORARY TABLE tmp会报错“Table 'tmp' already exists”,除非加IF NOT EXISTS;而更稳妥的做法是开头就DROP TEMPORARY TABLE IF EXISTS tmp。
注意点:
-
TRUNCATE TABLE tmp比DELETE FROM tmp快,但不释放磁盘空间;DROP则彻底清理 - 如果过程可能被嵌套调用(比如A调B,B也建同名临时表),必须确保命名唯一或用
DROP兜底 - 未
DROP的临时表在长连接中持续占用内存(尤其ENGINE=MEMORY时),可能引发OOM
游标+临时表组合时,变量命名别和字段名冲突
这是个隐蔽但高频出错点:当游标查询字段为id, name,而你又声明了同名变量DECLARE id INT; DECLARE name VARCHAR(50);,FETCH cur INTO id, name可能赋值失败或静默覆盖——MySQL会优先绑定字段名,导致变量始终为NULL或默认值。
安全做法:
- 变量名加前缀,如
c_id、c_name,和字段名物理隔离 - 在
FETCH后立刻加SELECT c_id, c_name;验证是否取到值 - 游标
DECLARE CONTINUE HANDLER FOR NOT FOUND必须设,否则循环可能无限卡住
临时表不是万能缓存,它解决的是“本会话内中间状态暂存”问题;真正该警惕的是把临时表当通用缓存用,或者在高并发场景下依赖它做跨请求共享——那已经超出它的设计边界。











