Oracle - 根据动态选择的列将数据从一行复制到另一行

Oracle - Copy data from one row to another based on dynamic selected columns

我有一个有两行的 table,我需要将 A 行中的一些数据复制到 B 行中。 我最担心的是所涉及的列在名称或编号上不是静态的(table 可以在列号上增加或缩小)。

我想出了一个我根本不喜欢的解决方案...

create or replace
PROCEDURE Z_COPY_TASK_ATT_PE
(
  par_oldRowId IN VARCHAR2,
  par_newRowId IN VARCHAR2
)
IS
  var_update VARCHAR2(4000 BYTE);
  var_crr_col_value_old VARCHAR2(10 BYTE);
  var_crr_select VARCHAR2(4000 BYTE);
BEGIN

  var_update := 'UPDATE PLANNING_ENTITY SET ';

  for i in (
            Select COLUMN_NAME 
            from user_tab_columns 
            where table_name='PLANNING_ENTITY' 
            and lower(COLUMN_NAME) like 'code%'
            )
  loop

    var_crr_select := 'select ' || i.column_name 
      || ' from planning_entity where planning_code = ''' 
      || par_oldRowId || ''''; 

    execute immediate var_crr_select
      into var_crr_col_value_old;

    var_update := var_update || i.column_name || ' = ''' || var_crr_col_value_old || ''', ';
  end loop;

  var_update := SUBSTR(var_update, 0, length(var_update)-2 );
  var_update := var_update || ' where planning_code = ''' || par_newRowId || '''';

  execute immediate var_update;
  commit;
END;

我的主要问题(至少对我而言)是我需要为循环的每次迭代制作 select。如果我可以制作一个 select 返回类似 column|name 的东西,那将是非常好的,然后我需要做的就是 i.column 和 i.value.

你怎么看?

===== Marmite Bomber Answer 之后的解决方案 =====

create or replace
PROCEDURE Z_COPY_TASK_ATT_PE
(
  par_oldRowId IN VARCHAR2,
  par_newRowId IN VARCHAR2
)
IS
  var_update VARCHAR2(4000 BYTE);
  var_columns varchar2(4000 BYTE);
BEGIN

  Select LISTAGG(COLUMN_NAME, ',') WITHIN group (order by column_name) into var_columns
  from user_tab_columns 
  where table_name='PLANNING_ENTITY' 
  and lower(COLUMN_NAME) like 'code%';

  var_update := 'UPDATE PLANNING_ENTITY PE1 SET (' || var_columns ||
    ') = (SELECT ' || var_columns || ' FROM PLANNING_ENTITY PE2 WHERE PE2.PLANNING_CODE = ''' || par_oldRowId ||
    ''') WHERE PE1.PLANNING_CODE = ''' || par_newRowId || '''';

  execute immediate var_update;
  commit;
END;

这个怎么样?

begin
  for i in (select * from PLANNING_ENTITY where planning_code = newrow) loop   
    update PLANNING_ENTITY set row = i where planning_code = oldrow;   
  end loop;
end;

反对者:

SQL> select version from v$instance;

VERSION
-----------------
11.2.0.4.0

SQL> create table it_works_in_oracle (n1 number, n2 number, n3 number);

Table created.

SQL> insert into it_works_in_oracle values(1, 1, 1);

1 row created.

SQL> insert into it_works_in_oracle values(2, 2, 2);

1 row created.

SQL> insert into it_works_in_oracle values(3, 3, 3);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from it_works_in_oracle;

        N1         N2         N3
---------- ---------- ----------
         1          1          1
         2          2          2
         3          3          3

SQL> set serveroutput on
SQL> declare
  oldrow number:=1;
  newrow number:=3;
begin
  for i in (select * from it_works_in_oracle where n1 = newrow) loop
    update it_works_in_oracle set row = i where n1 = oldrow;
  end loop;
end;  2    3    4    5    6    7    8
  9  /

PL/SQL procedure successfully completed.

SQL> select * from it_works_in_oracle;

        N1         N2         N3
---------- ---------- ----------
         3          3          3
         2          2          2
         3          3          3

基于这次更新

 UPDATE t1 a
 SET (code1, code2) = (
   SELECT code1,code2
   FROM t1 b
   WHERE b.planning_code = 'new')
 where a.planning_code = 'old';

您只需生成应更新的列名的逗号分隔列表,并在语句中应用两次。在示例中 code1,code2.

您可以这样做(未测试):

var_update := 'UPDATE PLANNING_ENTITY a SET (';

  FOR i IN (
            SELECT COLUMN_NAME 
            FROM USER_TAB_COLUMNS 
            WHERE table_name='PLANNING_ENTITY' 
            AND LOWER(COLUMN_NAME) LIKE 'code%'
            )
  LOOP
        var_update := var_update || i.column_name ||',';
  END LOOP;
  var_update := REGEXP_REPLACE(var_update, ',$', ')');
  var_update := var_update ||' = (SELECT ';
  FOR i IN (
            SELECT COLUMN_NAME 
            FROM USER_TAB_COLUMNS 
            WHERE table_name='PLANNING_ENTITY' 
            AND LOWER(COLUMN_NAME) LIKE 'code%'
            )
  LOOP
        var_update := var_update || i.column_name ||',';
  END LOOP;
  var_update := REGEXP_REPLACE(var_update, ',$');
  var_update := var_update || ' FROM PLANNING_ENTITY b WHERE planning_code = :newRowId) ';
  var_update := var_update || ' WHERE a.planning_code = :oldRowId';

  EXECUTE IMMEDIATE var_update USING par_newRowId, par_oldRowId;