私有临时表(ptt)不是pl/sql加速银弹,仅适用于单语句块内高频短生命周期中间结果暂存,须配on commit drop definition,不支持索引约束,不可替代gtt。
私有临时表(private temporary tables,ptt)在 oracle 18c 中不是“加速 pl/sql”的银弹,它只在特定场景下能显著减少锁争用和重编译开销——比如高频、短生命周期的中间结果暂存,且必须配合 on commit drop definition 使用,否则反而引入额外解析负担。
为什么不能直接替代全局临时表(GTT)?
私有临时表和全局临时表根本不是同一类对象:GTT 是会话级持久结构(定义存在,数据按事务或会话隔离),而 PTT 是完全内存化的、仅在当前语句块内可见的“一次性结构”。它的 DDL(如 CREATE PRIVATE TEMPORARY TABLE)每次执行都会触发硬解析+元数据注册,若在循环中反复创建,性能会比直接用嵌套表还差。
- PTT 的表名必须以
ORA$PTT_开头,且仅在当前会话的当前 SQL 或 PL/SQL 块中有效,退出即销毁 - GTT 的
TRUNCATE或INSERT /*+ APPEND */可复用执行计划;PTT 每次CREATE都是全新对象,优化器无法重用任何计划缓存 - PTT 不支持索引、约束、触发器,也不出现在
USER_TABLES中,只能通过V$PRIVATE_TEMP_TABLE_OBJECTS查看活跃实例
真正适合 PTT 的 PL/SQL 场景
只有当你要在单个复杂查询中“伪造”一个中间结果集,又不想污染会话状态、也不愿建 GTT 时,PTT 才有意义。典型例子是动态构建多层 WITH 子句依赖关系,或在函数内联 SQL 中隔离测试数据。
- 在存储过程中,用
EXECUTE IMMEDIATE 'CREATE PRIVATE TEMPORARY TABLE ORA$PTT_tmp AS SELECT ...'替代临时游标变量,避免多次打开/关闭游标带来的上下文切换 - 配合
DBMS_SQL动态执行时,用 PTT 承接未知结构的结果,再用DESCRIBE_COLUMNS分析列,比用REF CURSOR+FETCH更轻量 - 在递归 SQL(如层级遍历)中,用 PTT 存储每轮迭代的 ID 集合,避免
CONNECT BY的路径膨胀问题
常见错误:误用 PTT 导致硬解析风暴
开发者常把 PTT 当作“更快的 GTT”,在 FOR 循环里反复 CREATE → INSERT → DROP,这会迅速打爆共享池。Oracle 对 PTT 的 DDL 不做任何语句级缓存,CREATE PRIVATE TEMPORARY TABLE 每次都走完整解析流程。
- 错误写法:
FOR i IN 1..1000 LOOP EXECUTE IMMEDIATE 'CREATE PRIVATE TEMPORARY TABLE ORA$PTT_x AS SELECT :v FROM DUAL' USING i; END LOOP;—— 1000 次硬解析 - 正确思路:把逻辑压进单条 SQL,用 PTT 作为 WITH 子句的锚点,例如
WITH t AS (SELECT * FROM ORA$PTT_src) SELECT ... FROM t JOIN ... - 必须显式设置
ON COMMIT DROP DEFINITION,否则事务提交后表定义残留,下次同名创建会报ORA-65535: private temporary table already exists
PTT 的价值不在“快”,而在“干净”——它让 PL/SQL 能安全地在不修改数据库元数据的前提下,使用标准 SQL 语法操作临时结构。但凡需要跨语句复用、加索引、或批量插入,GTT + BULK COLLECT 仍是更稳的选择。











