You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Oracle 12c存储过程中的辅助表是否应改用全局临时表

问题概述
  • 现有Oracle 12c存储过程的常规结构:
PROCEDURE xxx AS 
   -- 变量声明
BEGIN
   -- 记录执行日志
   -- 创建1号中间计算表
   -- 记录执行日志
   -- 创建2号中间计算表
   -- 记录执行日志
   -- ...
   -- 创建最终结果表
   -- 记录执行日志
END xxx
  • 当前实现方式:1号到n-1号表为普通持久表,用于拆分复杂计算逻辑、优化执行性能,最终产出第n张结果表
  • 核心疑问:用普通表做中间存储是否合理?是否需要改用全局临时表?
  • 附实际业务代码:
create or replace PACKAGE BODY balance_mismatch_dca AS
    idproceduregeneral VARCHAR2(50);

    PROCEDURE aux_tables AS
        balance_zufi_dca_exists     INTEGER;
        balance_dfkkop_dca_exists   INTEGER;
    BEGIN
        idproceduregeneral := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(1));
        uxxxxxx_logs('Start Aux Tables', idproceduregeneral);
        -- 检查第一辅助表是否存在,存在则删除重建
        SELECT COUNT(*)
        INTO balance_zufi_dca_exists
        FROM sys.all_tables
        WHERE table_name = upper('balance_zufi_dca');

        IF balance_zufi_dca_exists = 1 THEN
            EXECUTE IMMEDIATE 'Drop Table balance_zufi_dca';
        END IF;
        EXECUTE IMMEDIATE q'[
            CREATE TABLE balance_zufi_dca AS
            WITH aux AS (
                SELECT vkont, vtref, MAX(zzmfvdat) AS lat
                FROM cdc.uap_zufi_t_dca_place@rbip
                WHERE zzextamount != 0
                GROUP BY vkont, vtref
            ), aux2 AS (
                SELECT a.vkont, a.vtref, a.lat,
                       MAX(b.zzextamount) AS amount_zufi,
                       MAX(b.zzplacref) AS dca,
                       MAX(zzplace_typ) AS placement
                FROM aux a
                JOIN cdc.uap_zufi_t_dca_place@rbip b 
                  ON b.vkont = a.vkont
                 AND b.vtref = a.vtref
                 AND b.zzmfvdat = a.lat
                GROUP BY a.vkont, a.vtref, a.lat
            )
            SELECT vkont,
                   SUM(amount_zufi) AS amount_zufi,
                   MAX(dca) AS dca,
                   MAX(placement) AS placement
            FROM aux2
            GROUP BY vkont
        ]';
        uxxxxxx_logs('End Balance Zufi DCA', idproceduregeneral);
        -- 检查第二辅助表是否存在,存在则删除重建
        SELECT COUNT(*)
        INTO balance_dfkkop_dca_exists
        FROM sys.all_tables
        WHERE table_name = upper('balance_dfkkop_dca');

        IF balance_dfkkop_dca_exists = 1 THEN
            EXECUTE IMMEDIATE 'Drop Table balance_dfkkop_dca';
        END IF;
        EXECUTE IMMEDIATE q'[
            Create Table balance_dfkkop_dca AS
            WITH aux AS (
                SELECT /*+ FULL(a) */
                       vkont,
                       betrw as amount_dfkkop
                FROM cdc.uap_dfkkop@rbip a
                WHERE augrs IS NULL
                  AND augst IS NULL
                  AND stakz IS NULL
                  AND abwbl IS NULL
                  AND (studt <= to_char(sysdate, 'YYYYMMDD') or studt is null)
                  AND faedn <= to_char(sysdate, 'YYYYMMDD')
            )
            SELECT * FROM aux
        ]';
        uxxxxxx_logs('End Balance DFKKOP DCA', idproceduregeneral);
    END aux_tables;

    PROCEDURE gen_view AS
    BEGIN
        idproceduregeneral := utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(1));
        -- 基于辅助表创建统计类永久视图
        EXECUTE IMMEDIATE q'[
            CREATE OR REPLACE VIEW vw_bm_dca_volumes AS
            WITH aux AS (
                SELECT a.vkont, a.amount_zufi,
                       SUM(nvl(b.amount_dfkkop, 0)) AS amount_dfkkop
                FROM balance_zufi_dca a
                LEFT JOIN balance_dfkkop_dca b ON b.vkont = a.vkont
                GROUP BY a.vkont, a.amount_zufi
            )
            SELECT CASE
                       WHEN amount_zufi = amount_dfkkop THEN 'Chased for the Correct Balance'
                       WHEN amount_zufi > amount_dfkkop THEN 'Chased for Higher Balance'
                       WHEN amount_zufi < amount_dfkkop THEN 'Chased for Lower Balance'
                       ELSE NULL
                   END AS status,
                   COUNT(1) AS ca_count,
                   round(RATIO_TO_REPORT(COUNT(1)) OVER() * 100, 2) perc
            FROM aux
            GROUP BY CASE
                WHEN amount_zufi = amount_dfkkop THEN 'Chased for the Correct Balance'
                WHEN amount_zufi > amount_dfkkop THEN 'Chased for Higher Balance'
                WHEN amount_zufi < amount_dfkkop THEN 'Chased for Lower Balance'
                ELSE NULL
            END
            ORDER BY COUNT(1) DESC
        ]';
        -- 基于辅助表创建明细类永久视图
        EXECUTE IMMEDIATE q'[
            CREATE OR REPLACE VIEW vw_bm_dca_data AS
            WITH aux AS (
                SELECT a.vkont, a.dca, a.placement, a.amount_zufi,
                       SUM(nvl(b.amount_dfkkop, 0)) AS amount_dfkkop
                FROM balance_zufi_dca a
                LEFT JOIN balance_dfkkop_dca b ON b.vkont = a.vkont
                GROUP BY a.vkont, a.dca, a.placement, a.amount_zufi
            )
            SELECT * FROM aux
            WHERE amount_zufi != amount_dfkkop
        ]';
        uxxxxxx_logs('End Views', idproceduregeneral);
    END gen_view;
END balance_mismatch_dca;
技术解答

当前普通表实现的固有问题

  • 固定DDL开销高:每次执行都要走完「检查表存在性 -> 删除旧表 -> CTAS建新表」的全流程,每次建表都会触发磁盘空间分配、高水位初始化、统计信息刷新的固定开销,执行效率低
  • 并发冲突严重:Drop/Create普通表的DDL操作会获取表排他锁,多会话并发触发存储过程时,会直接出现锁等待、执行失败的问题
  • 残留风险高:如果存储过程执行中途报错中断,之前创建的普通表不会自动清理,既占存储空间,也可能被其他会话误访问
  • 元数据冗余:普通表定义会被数据字典永久记录,长期运行会积累大量废弃表定义,提升数据库元数据维护成本

全局临时表的适配性说明

全局临时表的固有特性完全适配纯计算场景的中间存储需求:

  • 无重复DDL开销:全局临时表仅需创建一次,表结构永久存在,每次执行仅需写入数据,不需要反复删除、重建表结构
  • 天然支持并发:不同会话访问同一张全局临时表时,只能看到各自写入的独立数据集,完全不会互相阻塞
  • 自动清理无残留:根据定义的「提交时清空」「会话断开时清空」规则,数据会在对应节点自动释放,不会留下垃圾数据,也不会出现高水位反复增长的问题
  • 性能无损耗:全局临时表的访问性能与普通堆表基本一致,完全可以满足拆分复杂计算、优化执行计划的需求

关键适配误区:现有代码会基于辅助表创建永久视图。如果直接把辅助表替换为全局临时表,会出现逻辑错误:全局临时表的数据仅对写入数据的当前会话可见,当其他会话后续访问永久视图时,会因为读不到任何数据返回空结果,完全丧失查询能力。

针对当前代码的优化方向

  • 如果业务要求永久视图可以被所有会话访问、查询:不需要替换为全局临时表,直接把所有中间计算逻辑合并到视图的CTE定义中,去掉中间表存储环节即可。Oracle优化器会自动对多层嵌套CTE做执行路径优化,多数场景下性能不会弱于拆成中间表的实现。
  • 如果存储过程仅在单会话内完成计算、结果直接返回调用方,不需要后续其他会话访问中间结果:可以把两个辅助表改建为提交时自动清空的全局临时表,提前一次性定义好表结构,替换原有Drop/Create逻辑,改用直接INSERT的方式灌入数据。
  • 如果单步计算的CTE执行计划偏移、单步执行耗时过长:才需要用中间存储拆分计算逻辑,此时选择全局临时表的综合成本远低于反复Drop/Create普通表。

内容的提问来源于stack exchange,提问作者Javi Torre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.25 17:24:04