结论:create global temporary table 必须显式指定 on commit preserve rows 或 delete rows;gtt结构永久存在,部署时建一次即可,存储过程中仅需dml操作,数据天然会话隔离且不生成redo。

直接说结论:用 CREATE GLOBAL TEMPORARY TABLE 显式声明 ON COMMIT PRESERVE ROWS 或 ON COMMIT DELETE ROWS,并在存储过程开头确保表已存在——GTT 本身不需每次重建,结构永久、数据天然会话隔离。
为什么存储过程中不能每次 DROP + CREATE GTT
很多人误以为“临时表就得每次建”,结果在并发调用存储过程时遇到 ORA-00955: name is already used by an existing object。GTT 的表结构是永久对象,只创建一次即可;重复执行 CREATE GLOBAL TEMPORARY TABLE 会报错。真正需要隔离的只是数据,而 Oracle 已经自动保证:不同会话插入同一 GTT 的数据完全不可见、不锁表、不冲突。
- 正确做法是:在部署阶段一次性建好 GTT,存储过程中只做
INSERT/SELECT/TRUNCATE - 若真要清空当前会话数据,用
TRUNCATE TABLE xxx(比DELETE快,且不走回滚段) - 避免在存储过程里写
EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE ...'—— 除非你明确需要动态列,否则纯属引入风险
ON COMMIT DELETE ROWS 和 ON COMMIT PRESERVE ROWS 怎么选
这决定数据生命周期,直接影响存储过程能否跨事务使用临时数据。比如一个过程分三步:查源数据 → 加工 → 写结果,中间有 COMMIT,那必须选 PRESERVE ROWS,否则 COMMIT 后第二步就查不到前面插的数据。
-
ON COMMIT DELETE ROWS:适合单事务内分步处理,如批量校验后统一提交;COMMIT或ROLLBACK后数据自动消失 -
ON COMMIT PRESERVE ROWS:适合多事务协作场景,如过程内调用其他自治事务(AUTONOMOUS_TRANSACTION),或需在会话级缓存中间状态(例如分页游标暂存) - 注意:
PRESERVE ROWS下数据不会随事务结束消失,但会话断开(如连接池回收、显式DISCONNECT)时自动清空
存储过程里操作 GTT 的典型陷阱
最常见的不是语法错,而是逻辑错:忘了数据只属于当前会话,却写了依赖“全局可见”的判断逻辑。比如在 GTT 上建唯一索引后,仍假设“插入不报错=数据不存在”,其实只是本会话没插过——其他会话完全可能插了相同值,但这对你无影响,也不冲突。
- 不要对 GTT 做
ANALYZE或收集统计信息:GTT 没有真实数据分布,优化器会忽略统计或按固定规则估算 - 避免在 GTT 上建外键或触发器:Oracle 不支持,会报
ORA-14453 - 如果过程被多个应用模块复用,确认所有调用方都走同一套 GTT 定义(列名、类型、
ON COMMIT模式),否则容易因隐式转换或截断导致数据异常 - 测试时别只用单会话连数据库工具跑——必须模拟多会话并发调用,才能暴露隔离失效类问题(实际极少发生,但配置错误时可能)
最易被忽略的一点:GTT 的性能瓶颈几乎从不来自“临时表本身”,而是后续对它的 ORDER BY、GROUP BY 或大表关联——这些操作若超出 PGA_AGGREGATE_TARGET,就会 spill 到临时表空间,引发 I/O 等待。所以与其纠结 GTT 怎么建,不如先看执行计划里有没有 TEMP TABLE TRANSFORMATION 或大量 Sort 操作。











