Oracle的全局临时表(Global Temporary Table,简称GTT)是一种特殊的表结构:表的定义对所有人可见,但表中的数据只有当前会话自己能看到,会话结束后数据自动清除。它非常适合存放报表计算、批处理过程中的中间结果。不过在存储过程里创建全局临时表时,经常遇到权限报错或动态SQL使用不当的问题,本文结合实际场景详细讲解正确的做法。

一、全局临时表的基本语法与两种模式
创建全局临时表的核心语句是CREATE GLOBAL TEMPORARY TABLE,基本语法结构如下:
CREATE GLOBAL TEMPORARY TABLE temp_order_stats (
order_id NUMBER,
amount NUMBER(12,2),
created_at DATE
) ON COMMIT DELETE ROWS;关键在于最后的ON COMMIT子句,它决定了数据的生命周期,有两种选择。第一种是ON COMMIT DELETE ROWS,即事务级临时表:每当事务提交(COMMIT)或回滚时,表中的数据自动被清空,数据只在当前事务内可见。第二种是ON COMMIT PRESERVE ROWS,即会话级临时表:数据在整个会话期间一直保留,直到会话断开才自动删除。
两者的适用场景不同。如果是一次事务内完成计算并提交,用事务级即可,能保证每次提交后表是干净的;如果计算过程跨越多次提交,比如先插入中间数据,做完若干次提交后再汇总,就必须用会话级,否则数据在第一次COMMIT时就被清掉了,最终汇总结果会是空的。这是一个非常常见的坑,很多开发者发现临时表“莫名其妙没数据”,根源就在这里。
二、在存储过程中创建临时表的动态SQL写法
Oracle的存储过程内部不允许直接执行DDL语句(如CREATE TABLE),必须通过动态SQL来执行。因为DDL属于隐式提交操作,静态SQL无法处理,所以要把整条建表语句包装成字符串,交给EXECUTE IMMEDIATE执行。示例如下:
CREATE OR REPLACE PROCEDURE create_temp_table_proc AS
v_count NUMBER;
BEGIN
-- 先判断临时表是否已存在,避免重复创建报错
SELECT COUNT(*)
INTO v_count
FROM user_tables
WHERE table_name = 'TEMP_ORDER_STATS'
AND temporary = 'Y';
IF v_count = 0 THEN
EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE temp_order_stats (
order_id NUMBER,
amount NUMBER(12,2),
created_at DATE
) ON COMMIT PRESERVE ROWS';
DBMS_OUTPUT.PUT_LINE('临时表创建成功');
ELSE
DBMS_OUTPUT.PUT_LINE('临时表已存在,跳过创建');
END IF;
END create_temp_table_proc;这个写法有两个要点。第一,查询user_tables数据字典判断表是否存在,比直接执行建表再捕获异常更清晰可控;注意table_name字段存的是大写名称,条件中的表名必须大写。第二,temporary = 'Y'用于确认这是临时表,避免和同名普通表混淆。
还有一点需要说明:如果需要让临时表对所有会话统一可用,通常建议在数据库初始化阶段就一次性建好,而不是每个会话都去建。因为全局临时表的定义只有一份,重复创建会报ORA-00955对象名已被占用。存储过程里建表更适合初始化脚本或自动化部署场景。
三、权限问题:ORA-01031的根源与解决
在存储过程中执行建表语句时,最典型的报错是ORA-01031权限不足。明明用同一个账号在SQL*Plus里手动执行CREATE TABLE没问题,为什么放进存储过程就报错?原因在于Oracle存储过程默认以定义者权限运行,而定义者权限模式下,角色(ROLE)授予的权限是失效的,只有直接授予用户的系统权限才有效。
比如开发账号的CREATE TABLE权限通常来自RESOURCE角色,直接在SQL窗口执行时角色生效,可以建表;但存储过程内部角色被屏蔽,就失去了这个权限。解决办法是由DBA用sys或system账号直接授权:
-- 以管理员身份执行,直接授予系统权限而不是通过角色 GRANT CREATE ANY TABLE TO dev_user; GRANT CREATE PROCEDURE TO dev_user;
另一种思路是使用调用者权限,在过程声明中加上AUTHID CURRENT_USER,这样过程运行时采用调用者的权限和角色,只要调用者本身有建表权限即可。但要注意调用者权限会带来一些副作用,比如名称解析会指向调用者的Schema,可能引发其他对象找不到的问题,使用前要评估清楚。
四、临时表的使用规范与常见误区
全局临时表容易被误解的一点是“全局”二字的含义。它不代表数据全局共享,而是指表的定义是全局的、持久的,数据始终是会话私有的。两个会话同时往同一张全局临时表插数据,互相完全看不到对方的行,也不会产生锁冲突,这正是它优于普通表存放中间数据的地方。
使用时还有几条经验值得注意。首先,临时表上可以建索引,索引同样是会话私有的数据维护;其次,TRUNCATE一张会话级临时表只会清掉当前会话的数据,不会影响其他会话;再次,临时表的统计信息默认可能不准确,如果执行计划异常,可以考虑对会话数据做动态采样而不是收集全局统计信息。
最后梳理一下推荐做法:临时表尽量在应用部署阶段一次性创建,存储过程中只做插入和查询;确实需要在过程里建表时,配合存在性检查加动态SQL;事务边界要和ON COMMIT模式匹配。掌握这些要点后,全局临时表就能在报表统计、数据清洗等批量场景中稳定发挥作用。
Oracle全局临时表存储过程CREATE GLOBAL TEMPORARY TABLE修改时间:2026-09-02 16:46:56