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;
我有一个有两行的 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;