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

Oracle中如何用WITH子句的双SELECT实现多表合并/插入?

基于公共CTE实现多表合并/插入的解决方案

针对你需要从单一数据源通过CTE复用数据,同时完成两张表的合并/插入需求,以下三种Oracle环境下的可行方案:

方案1:PL/SQL集合缓存CTE结果

通过将CTE的结果一次性加载到PL/SQL集合中,再分别基于集合对两张表执行MERGE/INSERT操作,避免重复扫描源数据源。

步骤1:定义数据类型

CREATE OR REPLACE TYPE t_source_rec IS OBJECT (
    id NUMBER,
    month DATE,
    detail_col VARCHAR2(100),
    agg_col NUMBER
);
/
CREATE OR REPLACE TYPE t_source_tab IS TABLE OF t_source_rec;
/

步骤2:编写存储过程

CREATE OR REPLACE PROCEDURE daily_refresh_proc IS
    l_source_data t_source_tab;
BEGIN
    -- 一次性获取源数据并加载到集合
    WITH t1 AS (
        SELECT 
            id,
            TRUNC(create_date, 'MM') AS month,
            detail_info AS detail_col,
            amount AS agg_col
        FROM your_single_datasource
        WHERE create_date >= TRUNC(SYSDATE) - 1 -- 每日刷新过滤条件
    )
    SELECT t_source_rec(id, month, detail_col, agg_col)
    BULK COLLECT INTO l_source_data
    FROM t1;

    -- 处理明细目标表(按ID映射)
    MERGE INTO target_detail_table dt
    USING (
        SELECT id, detail_col 
        FROM TABLE(l_source_data)
    ) src
    ON (dt.id = src.id)
    WHEN MATCHED THEN
        UPDATE SET dt.detail_col = src.detail_col
    WHEN NOT MATCHED THEN
        INSERT (id, detail_col)
        VALUES (src.id, src.detail_col);

    -- 处理月聚合目标表(按月分组)
    MERGE INTO target_monthly_table mt
    USING (
        SELECT month, SUM(agg_col) AS total_amount
        FROM TABLE(l_source_data)
        GROUP BY month
    ) src
    ON (mt.month = src.month)
    WHEN MATCHED THEN
        UPDATE SET mt.total_amount = src.total_amount
    WHEN NOT MATCHED THEN
        INSERT (month, total_amount)
        VALUES (src.month, src.total_amount);

    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE;
END;
/

方案2:管道函数封装数据源

通过管道函数封装CTE查询逻辑,两张表的MERGE操作直接调用该函数,实现数据源的复用,无需重复扫描原表。

步骤1:创建管道函数

CREATE OR REPLACE FUNCTION get_daily_source_data RETURN t_source_tab PIPELINED IS
BEGIN
    WITH t1 AS (
        SELECT 
            id,
            TRUNC(create_date, 'MM') AS month,
            detail_info AS detail_col,
            amount AS agg_col
        FROM your_single_datasource
        WHERE create_date >= TRUNC(SYSDATE) - 1
    )
    FOR rec IN (SELECT * FROM t1) LOOP
        PIPE ROW(t_source_rec(rec.id, rec.month, rec.detail_col, rec.agg_col));
    END LOOP;
    RETURN;
END;
/

步骤2:编写存储过程

CREATE OR REPLACE PROCEDURE daily_refresh_proc IS
BEGIN
    -- 处理明细目标表
    MERGE INTO target_detail_table dt
    USING (
        SELECT id, detail_col 
        FROM TABLE(get_daily_source_data())
    ) src
    ON (dt.id = src.id)
    WHEN MATCHED THEN
        UPDATE SET dt.detail_col = src.detail_col
    WHEN NOT MATCHED THEN
        INSERT (id, detail_col)
        VALUES (src.id, src.detail_col);

    -- 处理月聚合目标表
    MERGE INTO target_monthly_table mt
    USING (
        SELECT month, SUM(agg_col) AS total_amount
        FROM TABLE(get_daily_source_data())
        GROUP BY month
    ) src
    ON (mt.month = src.month)
    WHEN MATCHED THEN
        UPDATE SET mt.total_amount = src.total_amount
    WHEN NOT MATCHED THEN
        INSERT (month, total_amount)
        VALUES (src.month, src.total_amount);

    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE;
END;
/

方案3:全局临时表(GTT)中转数据

使用Oracle全局临时表存储CTE结果,临时表数据仅当前会话可见,事务提交后自动清空,既避免了永久表的存储开销,又能简单实现数据复用,适合大数据量场景。

步骤1:创建全局临时表

CREATE GLOBAL TEMPORARY TABLE temp_daily_source (
    id NUMBER,
    month DATE,
    detail_col VARCHAR2(100),
    agg_col NUMBER
) ON COMMIT DELETE ROWS; -- 提交后自动清空数据

步骤2:编写存储过程

CREATE OR REPLACE PROCEDURE daily_refresh_proc IS
BEGIN
    -- 将CTE结果插入临时表
    INSERT INTO temp_daily_source
    WITH t1 AS (
        SELECT 
            id,
            TRUNC(create_date, 'MM') AS month,
            detail_info AS detail_col,
            amount AS agg_col
        FROM your_single_datasource
        WHERE create_date >= TRUNC(SYSDATE) - 1
    )
    SELECT id, month, detail_col, agg_col FROM t1;

    -- 处理明细目标表
    MERGE INTO target_detail_table dt
    USING (
        SELECT id, detail_col 
        FROM temp_daily_source
    ) src
    ON (dt.id = src.id)
    WHEN MATCHED THEN
        UPDATE SET dt.detail_col = src.detail_col
    WHEN NOT MATCHED THEN
        INSERT (id, detail_col)
        VALUES (src.id, src.detail_col);

    -- 处理月聚合目标表
    MERGE INTO target_monthly_table mt
    USING (
        SELECT month, SUM(agg_col) AS total_amount
        FROM temp_daily_source
        GROUP BY month
    ) src
    ON (mt.month = src.month)
    WHEN MATCHED THEN
        UPDATE SET mt.total_amount = src.total_amount
    WHEN NOT MATCHED THEN
        INSERT (month, total_amount)
        VALUES (src.month, src.total_amount);

    COMMIT; -- 提交后临时表数据自动清空
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE;
END;
/

方案选择建议

  • 数据量中等(百万级以内):优先选择PL/SQL集合,内存操作性能最优。
  • 数据量较大且不想占用过多内存:选择管道函数,按需取数减少内存压力。
  • 大数据量(千万级以上):优先选择全局临时表,Oracle对临时表的IO优化更成熟,稳定性更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 01:15:31