在 Oracle 里跑一条复杂 SQL 时,你有没有遇到过这样的情况: WITH 子查询被多次引用、星形转换里反复扫描事实表、GROUPING SET 不断重算同一批聚合…… CPU、I/O 都飙高,却只是在做重复工作。
Oracle 21c 之后引入的“游标持续时间临时表(Cursor Duration Temporary Table,简称 CDTT)”正是为了解决这个问题。 它像一块“中间结果缓存”,把重复计算的结果暂存在内存(必要时落盘),让后续步骤直接拿来用,从而显著降低资源消耗。
CDTT 要解决什么问题?
场景 | 重复计算示例 | 带来的开销 |
---|---|---|
WITH 子查询多次引用 | WITH q1 AS (SELECT …) SELECT * FROM q1 UNION ALL SELECT * FROM q1 … | q1 被多次执行 |
星形转换 | 事实表过滤后又被不同维度反复聚合 | 大表扫描多次 |
GROUPING SET | 同一批 GROUP BY 被拆成多份执行 | 聚合反复算 |
CDTT 把“中间结果”实体化成一张临时表,后续步骤直接 SELECT
这张表即可。
工作机制 6 步曲
- 计划选择 优化器发现某段子查询会被多次访问,于是生成使用 CDTT 的执行计划。
- 内存建表 以会话私有方式,在 PGA 中创建一张“只活于游标生命周期”的临时表(名字形如
SYS_TEMP_0FD9D6606_1AE004
)。 元数据 不在数据字典落地,查询DBA_OBJECTS
看不到。 - 查询重写 优化器把后续对子查询的引用全部改写成对这张临时表的扫描。
- 数据加载 先向内存中的临时表
LOAD AS SELECT
;PGA 不足时自动 spill 到临时段(类似排序 spill)。 - 执行并返回 后续步骤直接
TABLE ACCESS FULL
临时表,避免重复计算。 - 游标关闭即清理 游标关闭或从 Library Cache 淘汰时,内存/磁盘段一并
TRUNCATE
并释放。
执行计划里长什么样?
只要看到 TEMP TABLE TRANSFORMATION
与 LOAD AS SELECT (CURSOR DURATION MEMORY)
成对出现,就可以确认 CDTT 已启用:
--------------------------------------------------------------------
| Id | Operation | Name |
--------------------------------------------------------------------
| 1 | TEMP TABLE TRANSFORMATION | |
| 2 | LOAD AS SELECT (CURSOR DURATION MEMORY) | SYS_TEMP_0FD9D6606_... |
| 3 | HASH GROUP BY | |
| 4 | TABLE ACCESS FULL | EMPLOYEES |
| 5 | UNION-ALL | |
| 6 | VIEW | |
| 7 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6606_... |
| 8 | VIEW | |
| 9 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6606_... |
--------------------------------------------------------------------
SYS_TEMP_0FD9D6606_...
就是 CDTT。CURSOR DURATION MEMORY
关键字说明优先内存,PGA 不足才落盘。
一个极简示例
WITH
q1 AS (SELECT department_id, SUM(salary) sum_sal
FROM hr.employees
GROUP BY department_id),
q2 AS (SELECT * FROM q1),
q3 AS (SELECT * FROM q1)
SELECT * FROM q1
UNION ALL
SELECT * FROM q2
UNION ALL
SELECT * FROM q3;
执行计划如上所示,q1 的结果只算一次,被写入 CDTT,后三条 SELECT * FROM q1/q2/q3
全部变成对同一张临时表的全表扫描。
使用注意
- 只对当前会话可见:不用担心并发冲突。
- 无法手动干预:不能
DROP TABLE SYS_TEMP_...
,也不能加 hint 强制开关。 - 监控:
V$SQL_PLAN
可以看到OBJECT_NAME
字段出现SYS_TEMP_%
;V$PGASTAT
/V$TEMPSEG_USAGE
可观察 PGA 与临时段的使用。
- 性能对比:对重复子查询多的报表、ETL 作业,打开
STATISTICS_LEVEL=ALL
跑 AWR 报告,通常能看到 CPU、逻辑读明显下降。
下次看到执行计划里出现 CURSOR DURATION MEMORY
,别慌 —— 那是 Oracle 悄悄帮你把“重复劳动”存起来,让复杂 SQL 一次跑完、不再折腾。
把 CDTT 当成“游标级缓存”,用好它,你的长查询也能跑得既快又省。