【问题标题】:How to insert values from one table into another and then update the original table?如何将一个表中的值插入另一个表,然后更新原始表?
【发布时间】:2026-01-25 17:15:02
【问题描述】:

使用Oracle,有没有办法将一个表中的值插入到另一个表中,然后从插入的表中获取一个标识值并更新原始列中的一列?

TABLE_1 为空

ID    VALUE
-----------

TABLE_2 中的值 ...

ID    VALUE
-----------
0     Val 1
0     Val 2
0     Val 3

...插入到 TABLE_1(带有标识列)

ID    VALUE
-----------
1     Val 1
2     Val 2
3     Val 3

然后用 ID 更新 TABLE_2

ID    VALUE
-----------
1     Val 1
2     Val 2
3     Val 3

【问题讨论】:

    标签: oracle plsql


    【解决方案1】:

    您需要获得此类要求的程序。该解决方案使用 SELECT ... FOR UPDATE 锁定源表,以防止另一个会话获取我们想要提供新 ID 的记录。它还为我们提供了 WHERE CURRENT OF 语法,可以轻松识别我们需要更新的记录。

    此解决方案假设存在用于填充标识列的序列。我们还有其他可用的选项(包括 12C 中的自动增量),但 RETURNING 子句是获取新值的关键。

    declare
        cursor c2 is
            select * from table2
            for update of id;
        r2 c2%rowtype;
        new_id t1.id%type;
    begin
        open c2;
        loop
            fetch c2 in r2;
            exit when c2%notfound;
            /* new record */
            insert into t1
            values (t1_seq.nextval, t2.value)
            returning t1.id into new_id;
            /* update existing record with id*/
            update t2
            set id = new_id
            where current of c2;
        end loop;
        commit;
    end;
    /
    

    此解决方案是 Row-By-Row”,这是确保新 T1.ID 应用于 T2 中正确行的最简单方法。如果 T1 很小和/或这是一个 on -off 练习,这可能很好。但如果性能是一个问题,可以进行调整。

    【讨论】:

    • 谢谢,光标是我要走的路,我想知道我是否可以避免它,或者我是否可以做一个花哨的 MERGE。
    • 另外,非常感谢for update。我在游标中不知道这一点,我猜如果没有它,我会卡住一段时间。
    • 我确实有疑问,触发器会是更好的方法吗?即在主表上一个触发器在第二个表中插入值,然后在第二个表上使用另一个触发器来更新主表中的值,如果我错了,请纠正我。
    • @user75ponic - 触发器可以用于在table_1 中插入新记录,但对现有记录没有帮助。所以这似乎是一个新问题。
    【解决方案2】:

    如果表 2 中有很多行,我建议您使用 bulk collect。 它将帮助您提高数据库的性能。像这样:

    declare 
    type type_table2 is table of table2%rowtype index by binary_integer;
    vt_table2 type_table2;
    cursor cur_table2 is select * from table2;
    begin
    open cur_table2;
      loop
      fetch cur_table2 bulk collect into vt_table2 limit 500;
        for i in 1..vt_table2.count loop
          begin
            insert into table1
            values(i, vt.table2(i).value);
            update table2
            set id = i
            where current of cur_table2; 
          exception when other then
          excp := SQLERRM;
          dbms_output.put_line('Error:'||excp);
          end;
        end loop;
      exit when cur_table%notfound;
      end loop;
    close cur_table2;
    commit;
    exception when other then
      excp := SQLERRM;
      dbms_output.put_line('Error:'||excp);
    end;
    

    【讨论】:

    • 谢谢。我将运行这两种类型,看看是否有任何性能改进。如果您不介意我问,这里的批量收集是做什么的?
    • > BULK COLLECT:SELECT 语句通过一次提取检索多行,提高数据检索速度 您需要一个数组来保存内容并在本地搜索。 fetch cur_table2 bulk collect into vt_table2 limit 500;
    • 所以基本上,一次获取游标的所有记录,然后循环遍历?
    • > 具体来说,集合的内存存储在程序全局区 (PGA),而不是系统全局区 (SGA)。 SGA 内存由连接到 Oracle 数据库的所有会话共享,但为每个会话分配 PGA 内存。因此,如果一个程序需要 5MB 的内存来填充一个集合并且有 100 个同时连接,那么除了分配给 SGA 的内存之外,该程序还会消耗 500MB 的 PGA 内存 [链接] (oracle.com/technetwork/issue-archive/2008/08-mar/…)跨度>
    • @JonathanPeel 是的,以 500 条为单位获取所有记录,然后根据您的程序分析数据。
    最近更新 更多