GTT必须预建结构,禁止运行时CREATE;ON COMMIT DELETE ROWS适用于单事务,PRESERVE ROWS仅限跨事务同会话场景;TRUNCATE优于DELETE;需手动建索引并定期收集统计信息。

PL/SQL里别在运行时CREATE GTT
直接在存储过程里用EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE ...'看似灵活,实际是高危操作。Oracle要求GTT结构必须预建——DDL语句需由DBA或具备CREATE ANY TABLE权限的用户一次性执行完成。PL/SQL运行时调用会抛ORA-01031: insufficient privileges,尤其在连接池或受限schema下必败。
正确做法是:DBA提前建好表(如tmp_order_calc),PL/SQL中只做INSERT INTO tmp_order_calc、SELECT FROM tmp_order_calc等DML。结构永久存在,会话间自动隔离数据,无需重复建表。
ON COMMIT DELETE ROWS vs PRESERVE ROWS怎么选
选错ON COMMIT子句是PL/SQL中最隐蔽的bug来源之一。关键看数据生命周期是否跨事务:
-
ON COMMIT DELETE ROWS:适合单事务内分步处理,比如校验→转换→汇总→提交。COMMIT或ROLLBACK后自动清空,无残留风险 -
ON COMMIT PRESERVE ROWS:仅适用于明确需要跨事务、但严格绑定到同一数据库会话的场景(如长会话报表生成)。注意:PL/SQL包变量+PRESERVE ROWS组合易被误用,一旦会话复用(如连接池未清理),旧数据可能污染新逻辑
默认不写ON COMMIT会隐式采用DELETE ROWS,但显式声明更安全,避免团队理解偏差。
TRUNCATE比DELETE FROM更适合作为“重置”手段
在PL/SQL中反复填充临时表时,别用DELETE FROM tmp_tab清空——它走undo路径、触发行级锁、还生成大量undo redo。而TRUNCATE TABLE tmp_tab直接释放段,速度快且不依赖表是否为空。
即使表是ON COMMIT PRESERVE ROWS类型,TRUNCATE仍对当前会话生效;若需批量重填且数据量大,搭配INSERT /*+ APPEND */可跳过buffer cache,进一步提速。
小技巧:不必先查USER_TABLES判断表是否存在,直接TRUNCATE,捕获ORA-00942后再建表,代码更简洁可靠。
索引和统计信息不能忽略
临时表虽不写redo,但没索引+无统计信息时,十万行以上数据的JOIN或WHERE查询可能退化成全表扫描。PL/SQL中应在首次插入后、首次查询前手动收集:
DBMS_STATS.GATHER_TABLE_STATS(ownname => USER, tabname => 'TMP_ORDER_CALC');
索引可在GTT创建时一并建立(如CREATE INDEX idx_tmp_oid ON tmp_order_calc(order_id)),但注意:索引只能在表为空时创建,且APPEND插入期间索引会失效——如有强查询需求,建议插完再建索引,而非边插边维护。
真正容易被忽略的是:GTT的统计信息不会自动刷新,每次重填后若数据分布变化大(比如某次只插100行,下次插10万行),必须重新收集,否则优化器仍按旧统计走执行计划。


















