临时表不能在prepare/execute中直接引用,因动态sql在独立作用域执行;需显式drop temporary table if exists避免命名冲突;优先用insert into ... select而非游标;字段类型须显式定义,索引应合理添加。

临时表不能在PREPARE/EXECUTE中直接引用
这是最常踩的坑:你明明 CREATE TEMPORARY TABLE tmp_calc 成功了,后续却在 PREPARE stmt FROM @sql 里查不到它,报错 Table 'tmp_calc' doesn't exist。不是语法错,也不是权限问题,而是动态 SQL 在独立作用域执行,根本“看不见”上一步建的临时表。
静态 SQL(比如直接写 SELECT * FROM tmp_calc)完全没问题;但只要把表名拼进字符串再 EXECUTE,就失效。
- 别写
SET @sql = 'SELECT COUNT(*) FROM tmp_calc'; PREPARE stmt FROM @sql; EXECUTE stmt; - 要么改用静态 SQL 直接操作临时表
- 要么把整段逻辑拆到存储过程外,由应用层拼好 SQL 再执行
必须显式 DROP TEMPORARY TABLE IF EXISTS
临时表不会“自动清理”掉——会话不结束,它就一直占着名字和结构。连接池场景下,同一个连接反复调用同一存储过程,第二次执行时 CREATE TEMPORARY TABLE tmp_calc 就会报 Table 'tmp_calc' already exists。
所以每次创建前都得加 IF EXISTS,或者更稳妥地,在开头就 DROP TEMPORARY TABLE IF EXISTS tmp_calc。
-
DROP TEMPORARY TABLE只影响当前会话,安全 - 不要依赖“过程退出自动删”,连接复用会让这个假设崩塌
- 如果结构固定、数据量大,也可考虑
TRUNCATE TABLE tmp_calc替代重建,省去 DDL 开销
优先用 INSERT INTO ... SELECT,别用游标循环
很多人习惯在游标里一行行 INSERT INTO tmp VALUES (@a, @b),结果性能极差。MySQL 游标本身慢,加上每条 INSERT 都触发日志、锁和解析开销,远不如单条批量操作。
能写成 INSERT INTO tmp_table SELECT id, name, amount FROM orders WHERE ... GROUP BY ...,就别拆循环。
- 游标适合真正需要逐行判断逻辑的场景(比如带条件分支的复杂状态更新)
- 纯数据搬运或聚合,一律走
INSERT ... SELECT - 若数据量超内存,用主键范围切分(
WHERE id BETWEEN ? AND ?),而非LIMIT OFFSET(后者越往后越慢)
字段类型必须显式定义,别靠 CREATE ... AS SELECT 推导
CREATE TEMPORARY TABLE tmp AS SELECT ... 看起来省事,但 MySQL 推导的字段类型常出偏差:源字段是 VARCHAR(255),实际存了 300 字符,临时表就截断;DECIMAL 精度丢失导致后续求和不准;TIMESTAMP 被转成 DATETIME 丢时区信息。
显式声明结构才是稳的:
CREATE TEMPORARY TABLE tmp_calc ( id BIGINT UNSIGNED NOT NULL, amt DECIMAL(18,2) NOT NULL, dt DATE NOT NULL, INDEX idx_dt (dt), INDEX idx_id (id) );
- 索引该加就加,尤其用于
JOIN或WHERE的字段 - 避免用
TEXT或BLOB类型,它们强制磁盘临时表,拖慢速度 - 小数据量可加
ENGINE = MEMORY,但注意MEMORY不支持TEXT/BLOB和部分函数
临时表本身不难,难的是那些“看起来能跑通,上线后突然崩”的边界情况——动态 SQL 作用域、连接复用下的命名冲突、隐式类型推导的精度陷阱,这些才是真正在生产环境里卡住人的点。











